places.py
Nov 25, 2022 17:55:54 GMT -8
Post by Uncle Buddy on Nov 25, 2022 17:55:54 GMT -8
<drive>:\treebard\places.py Last Changed 2024-07-25
# places.py
import tkinter as tk
from PIL import Image, ImageTk
import sqlite3
from datetime import datetime
from base import Query, tree_path, tbard_path
from redraw import Redraw
from base import resize_scrolled_content
from widgets import (
configall, make_formats_dict, ScrolledDialog, open_message, LabelH3,
run_statusbar_tooltips, RightClickMenu, make_rc_menus, EntryAutoSinglePlace,
LabelButtonText, ButtonBigPic)
from gallery import Gallery
from notes import NotesDialog
from messages import places_msg
from messages_context_help import place_dupes_help_msg
from unigeds_queries import (
update_event_nested_place_unknown, insert_nested_place, delete_place_name,
select_place_id_with_name, insert_place_new, insert_place_name,
select_nested_place_inclusion, select_place_name_count,
update_event_nested_place, delete_nested_place, delete_place,
select_event_nested_place_id_count, select_nest_ids,
select_place_name_from_id, delete_place_name_by_string,
select_nested_place_id_inclusion, select_nested_place_smallest_nest,
delete_note_place_name_by_place_id, delete_note_place,
update_nested_place_nests, select_nested_place_by_nest0,
select_all_nested_places, update_place_name_spelling,
select_place_name_id_first_different, select_notes_links_place_name_id,
delete_notes_links_unlink_note)
import dev_tools as dt
from dev_tools import look, seeline
def update_place_lists(tree, cur, new_place=False):
tree.place_data = tree.get_place_values(new_place=new_place)
tree.create_single_place_lists()
def make_new_place(tree, name, conn, cur):
""" To make a new place, insert new rows to four database tables and
update the place lists.
"""
cur.execute(insert_place_new)
conn.commit()
new_place_id = cur.lastrowid
cur.execute(select_place_name_count, (new_place_id,))
count = cur.fetchone()[0]
cur.execute(insert_place_name, (name, new_place_id))
conn.commit()
new_place_name_id = cur.lastrowid
if count == 0:
cur.execute(
"INSERT INTO main_tbd (place_name_id) VALUES (?)",
(new_place_name_id,))
conn.commit()
now = datetime.now()
datestamp = now.strftime("%Y%m%d%H%M")
cur.execute(
"INSERT INTO traits_tbd (place_id, hint) VALUES (?, ?)",
(new_place_id, datestamp))
conn.commit()
update_place_lists(tree, cur, new_place=True)
return new_place_id
def get_nested_place_id_from_string(found, cur, place_string, tree):
def run_inner_loop(dkt):
for k,v in dkt.items():
if place_string != k:
continue
else:
nested_place_id = v["nested_place_id"]
return nested_place_id
nested_place_id = None
if found == 0:
return
elif found > 1:
print("All names of countries and other largest-enclosing-nests--as "
"well as all nested place strings in general--have to be unique.")
elif found == 1:
for dkt in tree.place_data:
nested_place_id = run_inner_loop(dkt)
if nested_place_id is None:
continue
else:
nested_place_id = nested_place_id
break
if nested_place_id:
if place_string in tree.prepended_places:
idx = tree.prepended_places.index(place_string)
del tree.prepended_places[idx]
tree.prepended_places.insert(0, place_string)
return nested_place_id
BUTTONS = ("ADD", "DELETE", "EDIT", "NOTE")
SELECT_NESTED_PLACE_INCLUSION_MAIN = '''
SELECT nested_place_id, a.place_name_text, b.place_name_text, c.place_name_text, d.place_name_text,
e.place_name_text, f.place_name_text, g.place_name_text, h.place_name_text, i.place_name_text
FROM nested_place
JOIN place_name as a ON a.place_id = nest0
JOIN place_name as b ON b.place_id = nest1
JOIN place_name as c ON c.place_id = nest2
JOIN place_name as d ON d.place_id = nest3
JOIN place_name as e ON e.place_id = nest4
JOIN place_name as f ON f.place_id = nest5
JOIN place_name as g ON g.place_id = nest6
JOIN place_name as h ON h.place_id = nest7
JOIN place_name as i ON i.place_id = nest8
JOIN main_tbd as j ON a.place_name_id = j.place_name_id
JOIN main_tbd as k ON b.place_name_id = k.place_name_id
JOIN main_tbd as l ON c.place_name_id = l.place_name_id
JOIN main_tbd as m ON d.place_name_id = m.place_name_id
JOIN main_tbd as n ON e.place_name_id = n.place_name_id
JOIN main_tbd as o ON f.place_name_id = o.place_name_id
JOIN main_tbd as p ON g.place_name_id = p.place_name_id
JOIN main_tbd as q ON h.place_name_id = q.place_name_id
JOIN main_tbd as r ON i.place_name_id = r.place_name_id
WHERE nest0 = ? OR nest1 = ? OR nest2 = ? OR nest3 = ? OR nest4 = ?
OR nest5 = ? OR nest6 = ? OR nest7 = ? OR nest8 = ?
'''
class PlaceEdit(tk.Toplevel):
def __init__(
self, master, tree, formats, main, current_action=None, *args, **kwargs):
tk.Toplevel.__init__(self, master, *args, **kwargs)
self.treebard = master
self.tree = tree
self.formats = formats
self.main = main
self.deleted_name = None
self.main_name = None
self.title("Place Edit Dialog")
self.make_inputs(current_action)
configall(self, self.formats)
def make_inputs(self, current_action):
ACTIONS = (self.add_place, self.delete_single_place,
self.edit_single_place, self.notate_single_place)
self.controls = tk.Frame(self)
self.controls.grid(column=0, row=0, sticky="news", padx=12, pady=12)
for child in self.controls.winfo_children():
child.destroy()
ACTIONS[current_action]()
def notate_single_place(self):
def ok_note1():
place_string = note_place_input.get().strip()
names = place_string.split(",")
if len(place_string) == 0:
self.destroy()
name_list = [i.strip() for i in names]
standardized_place_string = ", ".join(name_list)
conn = sqlite3.connect(self.tree.file)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
nested_place_id = None
found = self.tree.place_autofill_values.count(
standardized_place_string)
nested_place_id = get_nested_place_id_from_string(
found, cur, standardized_place_string, self.tree)
cur.execute(
''' SELECT place_id, place_name_text
FROM place_name
JOIN nested_place ON nest0 = place_id
WHERE nested_place_id = ?
''',
(nested_place_id,))
nest0, name0 = cur.fetchone()
self.open_place_notes((nest0, name0), standardized_place_string)
cur.close()
conn.close()
self.destroy()
def cancel_note1():
self.destroy()
self.title("Add a Place Note")
note1 = tk.Label(
self.controls, text="Place name:", anchor="w")
note_place_input = EntryAutoPlace(
self.controls, self.tree, autofill=True,
values=self.tree.place_autofill_values, width=36)
buttons = tk.Frame(self.controls)
oker = tk.Button(buttons, text="OK", width=7, command=ok_note1)
noper = tk.Button(buttons, text="CANCEL", width=7, command=cancel_note1)
note1.grid(column=0, row=0, sticky="e")
note_place_input.grid(column=1, row=0, padx=(6,0), sticky="w")
buttons.grid(column=1, row=2, sticky="e", pady=(12,0))
oker.grid(column=0, row=0)
noper.grid(column=1, row=0, padx=(6,0))
note_place_input.focus_set()
def open_place_notes(self, place_id, place_string):
NotesDialog(self.tree, self.treebard, header=f"Notes for {place_string}",
place_id=place_id, linked_element="place")
def edit_single_place(self):
""" List all places with this name, with their IDs and examples of
nested places in which they're included.
For user-selected place, e.g. "Paris, France" or "Paris, Texas":
Let the user change the spelling of an existing name.
Let the user delete an existing name.
Let the user add a new name.
Let the user choose which place name is main name for that place.
"""
def ok_edit1():
new_name = None
new_spelling = None
main_place_name_id = None
self.respell_input = None
place_id = self.editplacevar.get()
conn = sqlite3.connect(self.tree.file)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
new_name = self.adder_input.get().strip()
if "," in new_name:
return None
if new_name:
cur.execute(insert_place_name, (new_name, place_id))
conn.commit()
new_place_name_id = cur.lastrowid
if self.respell_input:
new_spelling = self.respell_input.get().strip()
if "," in new_spelling:
return None
if new_spelling:
cur.execute(
update_place_name_spelling,
(new_spelling, self.old_spelling))
conn.commit()
if self.deleted_name:
cur.execute(
''' SELECT main_tbd_id FROM place_name
JOIN main_tbd
ON main_tbd.place_name_id = place_name.place_name_id
WHERE place_name_text = ? AND place_id = ?
''',
(self.deleted_name, place_id))
result = cur.fetchone()
if result:
main_tbd_id = result[0]
cur.execute(
select_place_name_id_first_different,
(place_id, self.deleted_name))
new_place_name_id = cur.fetchone()
cur.execute(
''' UPDATE main_tbd
SET place_name_id = ?
WHERE main_tbd_id = ?
''',
(new_place_name_id, main_tbd_id))
conn.commit()
cur.execute(
select_notes_links_place_name_id,
(place_id, self.deleted_name))
results = cur.fetchall()
if len(results) != 0:
for notes_links_id in results:
cur.execute(
delete_notes_links_unlink_note, notes_links_id)
conn.commit()
cur.execute(
delete_place_name_by_string, (self.deleted_name, place_id))
conn.commit()
if self.main_name:
cur.execute(
''' SELECT main_tbd.place_name_id
FROM place_name
JOIN main_tbd
ON main_tbd.place_name_id = place_name.place_name_id
WHERE main_tbd.place_name_id IN (
SELECT place_name_id FROM place_name WHERE place_id = ?)
''',
(place_id,))
old_main_place_name_id = cur.fetchone()[0]
cur.execute(
''' SELECT place_name_id FROM place_name
WHERE place_name_text = ? AND place_id = ?
''',
(self.main_name, place_id))
result = cur.fetchone()
if result:
new_place_name_id = result[0]
cur.execute(
''' UPDATE main_tbd
SET place_name_id = ?
WHERE place_name_id = ?
''',
(new_place_name_id, old_main_place_name_id))
conn.commit()
cur.close()
conn.close()
self.destroy()
def respell(evt, frm):
widg = evt.widget
self.old_spelling = widg["text"]
self.respell_input = tk.Entry(frm, width=0)
self.respell_input.columnconfigure(0, weight=1)
self.respell_input.grid(column=0, row=0, sticky="ew", columnspan=10)
self.respell_input.insert(0, self.old_spelling)
configall(frm, self.formats)
def delete(evt, frm):
place_id = self.editplacevar.get()
widg = evt.widget
row = widg.grid_info()["row"]
self.deleted_name = widg["text"]
informer = tk.Label(
frm, text=(
f"The place name '{self.deleted_name}' (but not place ID #{place_id}) "
f"will be deleted on OK."))
informer.grid(column=0, row=row, sticky="ew", columnspan=10)
configall(frm, self.formats)
def make_main(evt, frm):
place_id = self.editplacevar.get()
widg = evt.widget
row = widg.grid_info()["row"]
self.main_name = widg["text"]
stg = f"'{self.main_name}' will become the display name for place ID #{place_id} on OK."
informer = tk.Label(frm, text=stg)
informer.grid(column=0, row=row, sticky="ew", columnspan=10)
configall(frm, self.formats)
def show_more():
last2 = False
conn = sqlite3.connect(self.tree.file)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
place_id = self.editplacevar.get()
cur.execute(select_place_name_from_id, (place_id,))
place_names = [i[0] for i in cur.fetchall()]
if len(place_names) > 1:
last2 = True
instrux = LabelH3(
more, self.formats, text=f"Edit place ID #{place_id}:")
instrux.grid(
column=0, row=0, sticky="ew", columnspan=2, pady=(12,6))
adder = tk.Label(more, text="Add Name:")
adder.grid(column=0, row=1, sticky="e", pady=(0,3))
self.adder_input = tk.Entry(more, width=0)
self.adder_input.grid(column=1, row=1, padx=(6,3), pady=(0,6), sticky="ew")
for dx, funx in enumerate(
("Respell Name", "Delete Name", "Main Name"), 2):
lab = tk.Label(more, text=f"{funx}:")
frm = tk.Frame(more)
for dinx, alias in enumerate(place_names):
width = len(alias) + 2
lb = LabelButtonText(
frm, text=alias, bd=1, relief="raised", takefocus=1,
font=self.formats["boilerplate"], width=width)
lb.grid(
column=dinx, row=0, sticky="ew", padx=(0,3), pady=3)
if dx == 2:
lb.bind(
"<Button-1>",
lambda evt, frm=frm: respell(evt, frm))
elif dx == 3:
lb.bind(
"<Button-1>", lambda evt, frm=frm:
delete(evt, frm))
elif dx == 4:
lb.bind("<Button-1>",
lambda evt, frm=frm: make_main(evt, frm))
lab.grid(column=0, row=dx, sticky="e", pady=3)
more.columnconfigure(1, weight=1)
if last2:
frm.grid(column=1, row=dx, sticky="ew", padx=(6,0))
elif last2 is False:
if dx == 2:
frm.grid(column=1, row=dx, sticky="ew", padx=(6,0))
for child in more.winfo_children():
grid_info = child.grid_info()
if grid_info.get("row") and grid_info["row"] > 2:
child.destroy()
for col in range(len(place_names)):
frm.columnconfigure(col, weight=1)
configall(self.controls, self.formats)
self.oker["state"] = "normal"
cur.close()
conn.close()
def get_input(evt):
nonlocal name
name = edit_single_input.get().strip()
edit_single_input["state"] = "disabled"
if len(name) == 0 or "," in name:
return None
conn = sqlite3.connect(self.tree.file)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
cur.execute(select_place_id_with_name, (name,))
dupes = [i[0] for i in cur.fetchall()]
if len(dupes) == 0:
return
for idx, place_id in enumerate(dupes, 1):
rad = tk.Radiobutton(
radframe, text=f"{name}: place ID #{place_id}", anchor="w",
value=place_id, variable=self.editplacevar,
command=show_more)
if len(dupes) == 1:
self.editplacevar.set(place_id)
show_more()
ex = tk.Frame(radframe)
cur.execute(
SELECT_NESTED_PLACE_INCLUSION_MAIN, tuple([place_id] * 9))
results = cur.fetchall()
if len(results) > 6:
results = results[0:5]
for indx, tup in enumerate(results):
example = ", ".join([i for i in tup[1:] if i != "unknown"])
lab = tk.Label(ex, text=f"{example}", anchor="w")
lab.grid(column=0, row=indx, sticky="w", padx=(24,0))
rad.grid(column=0, row=idx*2, sticky="w", pady=(12,0))
ex.grid(column=0, row=idx*2+1, sticky="w")
configall(self.controls, self.formats)
cur.close()
conn.close()
def cancel_edit1():
self.destroy()
name = None
self.editplacevar = tk.IntVar(None, 99999)
self.title("Edit an Existing Place")
top_frame = tk.Frame(self.controls)
edit1 = LabelH3(
top_frame, self.formats,
text="Place to edit (no commas):", anchor="w")
edit_single_input = tk.Entry(
top_frame, width=36, disabledbackground="#0d4a47",
disabledforeground="gray")
edit_single_input.bind("<FocusOut>", get_input)
radframe = tk.Frame(self.controls)
more = tk.Frame(self.controls)
buttons = tk.Frame(self.controls)
spacer = tk.Frame(buttons)
self.oker = tk.Button(
buttons, text="OK", width=7, command=ok_edit1, state="disabled")
noper = tk.Button(
buttons, text="CANCEL", width=7, command=cancel_edit1)
# children of self.controls
top_frame.grid(column=0, row=0, sticky="w", columnspan=2, padx=24)
radframe.grid(column=0, row=1)
more.grid(column=0, row=2, sticky="ew", padx=12, pady=12)
buttons.grid(
column=0, row=3, sticky="ew", pady=(12,24), padx=(0,24),
columnspan=3)
# children of top_frame
edit1.grid(column=0, row=0, sticky="w")
edit_single_input.grid(column=1, row=0, padx=(6,0), sticky="w")
# children of buttons
buttons.columnconfigure(0, weight=1)
spacer.grid(column=0, row=0, sticky="ew")
self.oker.grid(column=1, row=0)
noper.grid(column=2, row=0, padx=(6,0))
edit_single_input.focus_set()
def add_place(self):
def validate_place(name):
self.final = name
if self.final not in self.tree.place_autofill_values:
self.final = f"{self.final}+"
self.final = ValidatePlace(
self.treebard,
add_place_input,
"",
self.final,
self.formats,
self.tree,
self.main)
def ok_add1():
place_string = add_place_input.get().strip()
if len(place_string) == 0:
self.destroy()
validate_place(place_string)
self.withdraw()
def cancel_add1():
self.destroy()
self.title("Add a New Place")
add1 = tk.Label(
self.controls, text="Place name:", anchor="w")
add_place_input = EntryAutoPlace(
self.controls, self.tree, autofill=True,
values=self.tree.place_autofill_values, width=36)
buttons = tk.Frame(self.controls)
oker = tk.Button(buttons, text="OK", width=7, command=ok_add1)
noper = tk.Button(buttons, text="CANCEL", width=7, command=cancel_add1)
add1.grid(column=0, row=0, sticky="e")
add_place_input.grid(column=1, row=0, padx=(6,0), sticky="w")
buttons.grid(column=1, row=2, sticky="e", pady=(12,0))
oker.grid(column=0, row=0)
noper.grid(column=1, row=0, padx=(6,0))
add_place_input.focus_set()
def delete_single_place(self):
def ok_del1():
nesting = del_single_input.get().strip()
if len(nesting) == 0:
del_single_input.delete(0, "end")
del_single_input["bd"] = 3
del_single_input.focus_set()
return
conn = sqlite3.connect(self.tree.file)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
place_id_to_delete = self.get_id_to_delete(nesting, cur)
cur.execute(delete_note_place, (place_id_to_delete,))
conn.commit()
cur.execute(
"DELETE FROM traits_tbd WHERE place_id = ?", (place_id_to_delete,))
conn.commit()
cur.execute(delete_note_place_name_by_place_id, (place_id_to_delete,))
conn.commit()
cur.execute(
''' DELETE FROM main_tbd WHERE place_name_id in
(SELECT place_name_id FROM place_name WHERE place_id = ?)
''',
(place_id_to_delete,))
conn.commit()
cur.execute(delete_place_name, (place_id_to_delete,))
conn.commit()
cur.execute(
"SELECT nested_place_id FROM current_tbd WHERE current_tbd_id = 1")
placid = cur.fetchone()[0]
cur.execute(select_nested_place_by_nest0, (place_id_to_delete,))
nestings = [i[0] for i in cur.fetchall()]
cur.execute(select_all_nested_places)
all_nesting_ids = cur.fetchall()
for nested_place_id in nestings:
if nested_place_id == placid:
cur.execute(update_current_nested_place_to_1)
conn.commit()
placid = 1
cur.execute(select_nest_ids, (nested_place_id,))
old_nests = cur.fetchone()
if old_nests.count(1) != 8:
idx = old_nests.index(place_id_to_delete)
new_nests = list(old_nests)
del new_nests[idx]
new_nests.extend([1, nested_place_id])
new_nests_tup = tuple(new_nests)
if new_nests_tup[0:-1] not in all_nesting_ids:
cur.execute(update_nested_place_nests, new_nests_tup)
conn.commit()
else:
# Place being deleted is only nest in nesting; delete nesting.
cur.execute(update_event_nested_place, (1, nested_place_id))
conn.commit()
cur.execute(delete_nested_place, (nested_place_id,))
conn.commit()
cur.execute(
select_nested_place_id_inclusion, tuple([place_id_to_delete] * 9))
all_nestings_to_delete = cur.fetchall()
for nested_place_id in all_nestings_to_delete:
cur.execute(select_nest_ids, nested_place_id)
old_nests = cur.fetchone()
idx = old_nests.index(place_id_to_delete)
new_nests = list(old_nests)
del new_nests[idx]
new_nests.extend([1, nested_place_id[0]])
new_nests_tup = tuple(new_nests)
if new_nests_tup[0:-1] not in all_nesting_ids:
cur.execute(update_nested_place_nests, new_nests_tup)
conn.commit()
cur.execute(delete_place, (place_id_to_delete,))
conn.commit()
self.tree.placid = placid
rdw = Redraw(main=self.main, formats=self.formats, tree=self.tree)
rdw.redraw_place_tab()
rdw.redraw_person_tab()
cur.close()
conn.close()
self.destroy()
def cancel_del1():
self.destroy()
del1 = tk.Label(
self.controls,text="Place to delete:", width=48, anchor="w")
del_single_input = EntryAutoPlace(
self.controls, self.tree, autofill=True,
values=self.tree.place_autofill_values, width=0)
buttons = tk.Frame(self.controls)
oker = tk.Button(buttons, text="OK", width=7, command=ok_del1)
noper = tk.Button(buttons, text="CANCEL", width=7, command=cancel_del1)
del1.grid(column=0, row=0, sticky="ew", columnspan=2)
del_single_input.grid(
column=0, row=1, sticky="ew", padx=(6,0), columnspan=2)
buttons.grid(column=1, row=2, sticky="e", pady=(12,0))
oker.grid(column=0, row=0)
noper.grid(column=1, row=0, padx=(6,0))
del_single_input.focus_set()
def get_id_to_delete(self, nesting, cur):
place_id_to_delete = None
for dkt in self.tree.place_data:
for nested_place_string, innerdkt in dkt.items():
if nesting == nested_place_string:
current_nesting_id = innerdkt["nested_place_id"]
break
cur.execute(
select_nested_place_smallest_nest, (current_nesting_id,))
return cur.fetchone()[0]
class PlacesTab(tk.Frame):
def __init__(
self, master, tree, treebard, main, formats, cur, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.master = master
self.tree = tree
self.treebard = treebard
self.main = main
self.formats = formats
self.detail_labels = []
self.make_widgets(cur)
self.show_current_place()
def make_widgets(self, cur):
self.top_pic_place_button = ButtonBigPic(
self, command=self.open_place_gallery,
text="Add images to the current place\nin Preferences > Media tab.")
if len(self.tree.tree_id) != 0:
self.show_top_pic_nested_place(cur)
self.current_place_display = tk.Label(
self, justify="left", bg="pink", anchor="w")
self.current_place_details = tk.Frame(self, bg="red")
for text in ("latitude", "longitude", "description", "hint"):
lab = tk.Label(self.current_place_details)
lab["text"] = f"{text}:"
lab.grid(sticky="w")
self.detail_labels.append(lab)
place_change_frame = tk.Frame(self)
self.place_change_lab = tk.Label(
place_change_frame, text="Change current place to:")
self.new_current_place_input = EntryAutoPlace(
place_change_frame, self.tree,
values=self.tree.place_autofill_values,
autofill=True, width=60)
self.buttons = tk.Frame(place_change_frame)
self.current_place_changer = tk.Button(
self.buttons,
text="CHANGE CURRENT PLACE", width=31,
command=self.change_current_place)
self.current_place_deleter = tk.Button(
self.buttons,
text="DELETE CURRENT PLACE", width=31,
command=self.delete_current_place)
controls = tk.Frame(self)
instrux = tk.Label(
controls, anchor="w",
text="Select an action in regards to a place or place name:")
buttons = tk.Frame(controls)
for column, text in enumerate(BUTTONS):
action = tk.Button(
buttons, text=text, width=8,
command=lambda column=column:
self.open_place_edit_dialog(column))
buttons.columnconfigure(column, weight=1)
action.grid(row=0, column=column, sticky="e", padx=3)
# children of self
self.columnconfigure(0, weight=0)
self.columnconfigure(1, weight=1)
self.top_pic_place_button.grid(
column=0, row=0, sticky="w", padx=(0,24), rowspan=2)
self.current_place_display.grid(column=1, row=0, pady=12, sticky="ew")
self.current_place_details.grid(
column=1, row=1, pady=12, padx=24, sticky="ew")
place_change_frame.grid(
column=0, row=2, sticky="news", padx=12, pady=12, columnspan=2)
controls.grid(
column=0, row=3, sticky="news", padx=12, pady=12, columnspan=2)
# children of place_change_frame
self.place_change_lab.grid(
column=0, row=0, sticky="w", padx=(12,0), pady=(0,12))
self.new_current_place_input.grid(
column=1, row=0, sticky="ew", padx=(6,0), pady=(0,12))
self.buttons.grid(column=1, row=1, sticky="e", padx=(6,0), pady=(0,12))
self.current_place_changer.grid(column=0, row=0, sticky="e")
self.current_place_deleter.grid(
column=1, row=0, sticky="e", padx=(6,0))
# children of controls
controls.columnconfigure(1, weight=1)
instrux.grid(column=0, row=0, pady=(0,12), sticky="w")
buttons.grid(column=1, row=0, pady=(0,12), sticky="ew")
def show_current_place(self):
if self.tree.placid is None:
self.tree.placid = 1
conn = sqlite3.connect(self.tree.file)
cur = conn.cursor()
cur.execute(select_nest_ids, (self.tree.placid,))
nested_places = list(cur.fetchone())
nest0 = nested_places[0]
cur.execute(
''' SELECT latitude, longitude, description, hint
FROM place
JOIN traits_tbd ON traits_tbd.place_id = place.place_id
WHERE traits_tbd.place_id = ?
''',
(nest0,))
details = cur.fetchone()
for idx, idnum in enumerate(list(nested_places)):
cur.execute(
''' SELECT place_name_text
FROM place_name
JOIN main_tbd
ON main_tbd.place_name_id = place_name.place_name_id
WHERE place_id = ?
''',
(idnum,))
name = cur.fetchone()[0]
nested_places[idx] = name
nested_places = [i for i in nested_places if i != "unknown"]
nested_place_name = ", ".join(nested_places)
self.new_current_place_input.delete(0, 'end')
self.show_top_pic_nested_place(cur)
self.current_place_display.config(
text=f"Current Nested Place:\n{nested_place_name}")
for idx, text in enumerate(
("latitude", "longitude", "description", "hint")):
lab = tk.Label(self.current_place_details)
if details:
self.detail_labels[idx]["text"] = f"{text}: {details[idx]}"
cur.close()
conn.close()
def show_top_pic_nested_place(self, cur):
cur.execute(
''' SELECT file_name
FROM media_links
JOIN nested_place
ON media_links.nested_place_id = nested_place.nested_place_id
JOIN media
ON media.media_id = media_links.media_id
JOIN main_tbd
ON main_tbd.media_links_id = media_links.media_links_id
WHERE media_links.nested_place_id = ?
''',
(self.tree.placid,))
top_pic_place = cur.fetchone()
if top_pic_place:
img_stg = ''.join(top_pic_place)
new_stg = f"{tree_path}/{self.tree.tree_id}/images/{img_stg}"
elif self.main.use_default_images:
img_stg = self.main.nested_place_image
new_stg = f'{tree_path}/settings/images/{img_stg}'
elif self.use_default_images is False:
self.top_pic_place_button.config(image="")
self.top_pic_place_button.image = ""
return
top = Image.open(new_stg)
top = self.main.resize_top_pic(top, new_stg)
img1 = ImageTk.PhotoImage(top, master=self.main.canvas)
self.top_pic_place_button.config(image=img1)
self.top_pic_place_button.image = img1
def open_place_edit_dialog(self, column):
self.place_edit_dialog = PlaceEdit(
self.treebard, self.tree, self.formats, self.main,
current_action=column)
def open_place_gallery(self):
Gallery(
self.tree, self.main, self.main.main_tabs,
self.main.main_tabs.store['graphics'],
self.main.SCREEN_SIZE, mode="place", tab=self.master)
def change_current_place(self):
conn = sqlite3.connect(self.tree.file)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
nested_place_string = self.new_current_place_input.get().strip()
if nested_place_string not in self.tree.place_autofill_values:
final = f"{nested_place_string}+"
else:
final = nested_place_string
ValidatePlace(
self.treebard, self.new_current_place_input, None, final,
self.formats, self.tree, self.main)
rdw = Redraw(tree=self.tree, main=self.main, formats=self.formats)
rdw.redraw_place_tab()
cur.close()
conn.close()
def delete_current_place(self):
conn = sqlite3.connect(self.tree.file)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
cur.execute(select_current_nested_place_id)
old_current = cur.fetchone()[0]
cur.execute(select_nested_place_id_not_this, (old_current,))
new_current = cur.fetchone()[0]
cur.execute(update_current_nested_place, (new_current,))
conn.commit()
cur.execute(update_event_nested_place_unknown, (old_current,))
conn.commit()
cur.execute(delete_notes_links_nesting, (old_current,))
conn.commit()
cur.execute(delete_nested_place, (old_current,))
conn.commit()
self.tree.placid = new_current
rdw = Redraw(tree=self.tree, main=self, formats=self.formats)
rdw.redraw_place_tab()
cur.close()
conn.close()
SINGLE_PLACE_VALUES = {"single_place_name": "", "place_id": 0, "make_new": False}
class ValidatePlace():
def __init__(
self, treebard, inwidg, initial, final, formats, tree, main,
event=None):
self.treebard = treebard
self.inwidg = inwidg
self.initial = initial
self.final = final
self.formats = formats
self.tree = tree
self.main = main
self.event = event
self.new_places = []
self.single_place_values = dict(SINGLE_PLACE_VALUES)
self.validate_place()
def validate_place(self):
conn = sqlite3.connect(self.tree.file)
conn.execute('PRAGMA foreign_keys = 1')
cur = conn.cursor()
if self.initial:
lenstart = len(self.initial)
else:
lenstart = 0
lenfinal = len(self.final)
place_list1 = self.final.replace(",,", ",").replace(" ", " ").replace(
"+", "", -1)
place_list = place_list1.split(",")
self.full_place_list = [i.strip() for i in place_list]
self.shrinking_place_list = list(self.full_place_list)
self.nesting_length = len(self.full_place_list)
if self.initial == self.final:
return
if self.final.endswith("+"):
self.dispatch_tasks(new=True)
self.final = self.final.replace("+", "", -1)
self.final = self.final
elif self.initial is None:
self.initial = self.inwidg.get()
self.dispatch_tasks(change_current=True)
elif lenstart > 0 and lenfinal == 0:
self.dispatch_tasks(unlink=True)
elif lenstart > 0 and lenfinal > 0:
self.dispatch_tasks(replace=True)
elif lenstart == 0 and lenfinal > 0:
self.dispatch_tasks(add=True)
else:
print("line", look(seeline()).lineno, "case not handled:")
cur.close()
conn.close()
def dispatch_tasks(
self, new=False, add=False,
replace=False, unlink=False, change_current=False):
tree_db = f"{tree_path}/{self.tree.tree_id}/{self.tree.tree_id}.tbd"
conn = sqlite3.connect(tree_db)
cur = conn.cursor()
if new:
self.open_dupe_place_dlg()
elif add or replace or change_current:
found = self.tree.place_autofill_values.count(self.final)
nested_place_id = get_nested_place_id_from_string(
found, cur, self.final, self.tree)
if add or replace:
cur.execute(
update_event_nested_place, (nested_place_id, self.event,))
conn.commit()
cur.execute(
''' SELECT nested_place_id
FROM current_tbd
WHERE current_tbd_id = 1
''')
self.tree.placid = cur.fetchone()[0]
rdw = Redraw(main=self.main, tree=self.tree, formats=self.formats)
rdw.redraw_events_table()
rdw.redraw_place_tab()
elif change_current:
cur.execute(
''' UPDATE current_tbd
SET nested_place_id = ?
WHERE current_tbd_id = 1
''',
(nested_place_id,))
conn.commit()
self.tree.placid = nested_place_id
rdw = Redraw(main=self.main, tree=self.tree, formats=self.formats)
rdw.redraw_place_tab()
elif unlink:
cur.execute(update_event_nested_place_unknown, (self.event,))
conn.commit()
cur.execute(
''' SELECT nested_place_id
FROM current_tbd
WHERE current_tbd_id = 1
''')
self.tree.placid = cur.fetchone()[0]
rdw = Redraw(main=self.main, tree=self.tree, formats=self.formats)
rdw.redraw_gui()
cur.close()
conn.close()
def open_dupe_place_dlg(self):
conn = sqlite3.connect(self.tree.file)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
self.name = self.shrinking_place_list.pop(0)
cur.execute(select_place_id_with_name, (self.name,))
dupe_ids = [i[0] for i in cur.fetchall()]
current_single_place_dupe_count = len(dupe_ids)
self.single_place_values["single_place_name"] = self.name
if self.inwidg.winfo_class() == "Text":
got = self.inwidg.get("1.0", "end-1c")
elif self.inwidg.winfo_class() == "Entry":
got = self.inwidg.get()
if len(got) == 0:
cur.execute(update_event_nested_place, (1, self.event))
conn.commit()
cur.close()
conn.close()
return
PlaceDupes(
self.treebard, self.formats, self.tree, self.inwidg,
self.main, self.initial, self.final, self.name, dupe_ids,
current_single_place_dupe_count, self.single_place_values,
self.nesting_length, self.shrinking_place_list,
self.full_place_list, event=self.event)
class PlaceDupes(ScrolledDialog):
def __init__(
self, master, formats, tree, inwidg, main, initial, final, name,
dupe_ids, current_single_place_dupe_count, single_place_values,
nesting_length, shrinking_place_list, full_place_list, event=None,
*args, **kwargs):
ScrolledDialog.__init__(self, master, *args, **kwargs)
self.treebard = master
self.formats = formats
self.tree = tree
self.inwidg = inwidg
self.main = main
self.initial = initial
self.name = name
self.dupe_ids = dupe_ids
self.current_single_place_dupe_count = current_single_place_dupe_count
self.single_place_values = single_place_values
self.nesting_length = nesting_length
self.shrinking_place_list = shrinking_place_list
self.full_place_list = full_place_list
self.event = event
conn = sqlite3.connect(self.tree.file)
cur = conn.cursor()
self.opened_dialogs = 0
self.selections = []
self.tree.title(
f"Duplicate Place Dialog input: {final.replace('+', '', 1)}")
self.geometry("+120+24")
self.rc_menu = RightClickMenu(self, self.treebard)
self.repeat_per_single_place(cur)
ScrolledDialog.bind_canvas_to_mousewheel(self.canvas)
configall(self, self.formats)
self.resize_scrolled_content(self, self.canvas, add_x=16, add_y=24)
self.lift()
self.focus_force()
for child in self.window.winfo_children():
if child["takefocus"] == 1:
child.focus_set()
break
cur.close()
conn.close()
def make_widgets(self):
self.window.columnconfigure(0, weight=1)
self.window.rowconfigure(0, weight=1)
self.content = tk.Frame(self.window)
self.header = tk.Label(
self.content, text=f"Which {self.name}?",
justify='left', wraplength=600)
self.radframe = tk.Frame(self.content)
buttons = tk.Frame(self.content)
okbutt = tk.Button(
buttons, text="OK", width=7, command=self.ok_place_dialogs)
cancelbutt = tk.Button(
buttons, text="CANCEL", width=7,
command=lambda dialog=self: self.cancel_place_dialogs(dialog))
# children of self.window
self.window.columnconfigure(0, weight=1)
self.window.rowconfigure(1, weight=1)
self.content.grid(column=0, row=0, sticky="news")
# children of self.content
self.header.grid(
column=0, row=0, sticky='news', ipady=6, ipadx=6,
columnspan=2)#, padx=12, pady=12
self.radframe.grid(
column=0, row=1, sticky="news", columnspan=2)#, pady=(12,0)
buttons.grid(column=1, row=2, sticky='se', pady=(12,6))#, padx=12, pady=12
# children of buttons
okbutt.grid(column=0, row=0)
cancelbutt.grid(column=1, row=0, padx=(3,0))
self.maxsize(
int(self.winfo_screenwidth() * 0.90),
int(self.winfo_screenheight() * 0.90))
def make_inputs(self, cur):
""" Make a section of widgets for each duplicate of a place name. """
self.dupevar = tk.IntVar(None, 99)
self.radnew = tk.Radiobutton(
self.radframe,
text=f"New place named {self.name}",
variable=self.dupevar,
value=1000, )
self.radnew.config(command=lambda widg=self.radnew:
self.handle_radiobutton_click(widg, radio="new"))
self.radnew.grid(column=0, row=1, sticky="w")
row = 2
for num in self.dupe_ids:
cur.execute("SELECT hint FROM traits_tbd WHERE place_id = ?", (num,))
result = cur.fetchone()
if result:
hint = result[0]
else:
hint = "none"
if hint:
text=f"{self.name} (hint: '{hint}')"
else:
text=self.name
frm = tk.LabelFrame(
self.radframe,
text=(f"Existing place named {self.name} and nested places "
f"which include this place:"))
rad = tk.Radiobutton(
frm, text=text,
variable=self.dupevar,
value=row-2)
frm.grid(column=0, row=row, sticky="ew", pady=(0,12))
rad.grid(column=0, row=0, sticky="w")
rad.config(
command=lambda widg=rad: self.handle_radiobutton_click(
widg, radio="existing"))
self.show_examples(num, frm, cur)
row += 1
visited = (
(self.content,
"New & Duplicate Places Dialog",
"Treebard cares about places too."),
(self.header,
"Duplicate Places Header",
"Treebard tracks same-named places separately."),
(self.radnew,
"New Place Selector",
"Make a new place by this name."),)
run_statusbar_tooltips(
visited,
self.statusbar.status_label,
self.statusbar.tooltip_label, self)
rcm_widgets = (
self.content, self.header, self.radnew)
make_rc_menus(
rcm_widgets,
self.rc_menu,
place_dupes_help_msg)
def handle_radiobutton_click(self, widg=None, radio=None):
dupevarget = self.dupevar.get()
if dupevarget == 99:
pass
elif dupevarget != 1000:
self.single_place_values["make_new"] = False
self.single_place_values["place_id"] = self.dupe_ids[dupevarget]
elif dupevarget == 1000:
self.single_place_values["make_new"] = True
self.single_place_values["place_id"] = 0
else:
print("line", look(seeline()).lineno, "dupevarget:", dupevarget)
def repeat_per_single_place(self, cur):
self.make_widgets()
self.make_inputs(cur)
if self.inwidg.winfo_class() == "Text":
got = self.inwidg.get("1.0", "end-1c")
elif self.inwidg.winfo_class() == "Entry":
got = self.inwidg.get()
configall(self, self.formats)
self.resize_scrolled_content(self, self.canvas, add_x=16, add_y=24)
def display_next(self, conn, cur):
self.name = self.shrinking_place_list.pop(0)
cur.execute(select_place_id_with_name, (self.name,))
self.dupe_ids = [i[0] for i in cur.fetchall()]
self.current_single_place_dupe_count = len(self.dupe_ids)
self.single_place_values["single_place_name"] = self.name
if self.inwidg.winfo_class() == "Text":
got = self.inwidg.get("1.0", "end-1c")
elif self.inwidg.winfo_class() == "Entry":
got = self.inwidg.get()
if self.event and len(got) == 0:
cur.execute(update_event_nested_place, (1, self.event))
conn.commit()
self.repeat_per_single_place(cur)
def ok_place_dialogs(self):
def err_done1():
msg1[0].grab_release()
msg1[0].destroy()
self.opened_dialogs += 1
conn = sqlite3.connect(self.tree.file)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
if self.dupevar.get() == 99:
msg1 = open_message(self.treebard, places_msg[0],
"No Selection was Made", "OK", formats=self.formats)
msg1[0].grab_set()
msg1[2].config(command=err_done1)
return
for child in self.window.winfo_children():
child.destroy()
self.selections.append(dict(self.single_place_values))
self.single_place_values = dict(self.single_place_values)
if self.opened_dialogs == self.nesting_length:
self.update_db()
self.cancel_place_dialogs(dialog=self)
subclass = type(self.inwidg).__name__
if subclass in ("CellAutoPlace", "EntryAutoPlace"):
self.main.places_tab_content.new_current_place_input.focus_set()
else:
print("line", look(seeline()).lineno, "case_not_handled:")
cur.close()
conn.close()
else:
self.display_next(conn, cur)
def update_db(self):
conn = sqlite3.connect(self.tree.file)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
for idx,dkt in enumerate(list(self.selections)):
if dkt["make_new"]:
new_place_id = make_new_place(
self.tree, self.full_place_list[idx], conn, cur)
self.current_single_place_dupe_count += 1
self.selections[idx]["place_id"] = new_place_id
self.selections[idx]["make_new"] = False
nested_ids = []
for dkt in self.selections:
place_id = dkt["place_id"]
nested_ids.append(place_id)
length = len(nested_ids)
new_nesting = nested_ids + [1] * (9 - length)
cur.execute(insert_nested_place, tuple(new_nesting))
conn.commit()
new_nested_place_id = cur.lastrowid
if self.event:
cur.execute(
update_event_nested_place,
(new_nested_place_id, self.event,))
conn.commit()
self.tree.place_data = self.tree.get_place_values(new_place=True)
self.tree.single_place_autofill_values = self.tree.create_single_place_lists()
cur.close()
conn.close()
def cancel_place_dialogs(self, dialog=None, cell=None):
if dialog:
dialog.destroy()
def show_examples(self, num, frm, cur):
""" List the nestings that use the current place and occur most
commonly in the conclusions table in `commonest`. If `commonest`
is empty, list nestings that use the current place and
exist but don't occur in the conclusions table in
`unused_nestings`. If `commonest` and `unused_nestings` are both
empty, change text to "".
"""
cur.execute(select_nested_place_inclusion, tuple([num] * 9,))
inclusions = [list(i) for i in cur.fetchall()]
copy = list(inclusions)
for indx, lst in enumerate(copy):
nested_place_id = lst[0]
lst = [i for i in lst[1:] if i != "unknown"]
inclusions[indx] = lst
examples = {}
for idx,lst in enumerate(copy):
examples[lst[0]] = inclusions[idx]
counted = []
cur.execute(select_nested_place_id_inclusion, tuple([num] * 9,))
for nested_place_id in cur.fetchall():
cur.execute(select_event_nested_place_id_count, nested_place_id)
counted.append(cur.fetchone())
counted = sorted(counted, key=lambda i: i[1], reverse=True)
commonest = [tup[0] for tup in counted[0:3] if tup[0]]
if len(commonest) == 0:
unused_nestings = self.get_unused_nestings(inclusions)
if len(unused_nestings) != 0:
for text in unused_nestings:
lab = tk.Label(frm, text=text, anchor="w")
lab.grid(column=0, row=idx+2, sticky="ew", padx=(36,0))
else:
lab = tk.Label(frm, text="none", anchor="w")
lab.grid(column=0, row=idx+2, sticky="ew", padx=(36,0))
return
for idx,num in enumerate(commonest):
for k,v in examples.items():
if num == k:
lab = tk.Label(frm, text=", ".join(v), anchor="w")
lab.grid(column=0, row=idx+2, sticky="ew")#, padx=(36,0)
break
def get_unused_nestings(self, inclusions):
unused_nestings= []
for idx,lst in enumerate(list(inclusions)):
stg = ", ".join(lst)
unused_nestings.append(stg)
return unused_nestings
class PlacesCopy(tk.Frame):
def __init__(self, master, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.master = master
self.make_widgets()
def make_widgets(self):
headlab = tk.Label(
self,
text="Copy places from one tree to another.\nSelect one tree "
"from each column.",
justify="left", wraplength=600, bd=1, relief="raised")
from_column = tk.Frame(self, bd=1)
to_column = tk.Frame(self, bd=1)
from_head = tk.Label(from_column, text="FROM:", anchor="w")
to_head = tk.Label(to_column, text="TO:", anchor="w")
ok_button = tk.Button(
self, text="OK",
command=lambda from_column=from_column, to_column=to_column:
self.ok_export_places(from_column, to_column),
width=7)
self.rowconfigure(1, weight=1)
self.master.columnconfigure(0, weight=1)
headlab.grid(
column=0, row=0, columnspan=2, pady=12, ipadx=6, ipady=6, padx=12)
from_column.grid(column=0, row=1, sticky="new")
to_column.grid(column=1, row=1, sticky="new", padx=(24,0))
ok_button.grid(column=1, row=2, sticky="e", pady=12)
from_head.grid(column=0, row=0, sticky="ew", padx=12)
to_head.grid(column=0, row=0, sticky="ew", padx=12)
conx = sqlite3.connect(tbard_path)
curx = conx.cursor()
curx.execute("SELECT dub, family_tree_id FROM family_tree")
trees = curx.fetchall()
self.all_trees = sorted([list(i) for i in trees], key=lambda i: i[0])
for column in from_column, to_column:
for lst in self.all_trees:
lab = tk.Label(column, text=lst[0], anchor="w")
lab.grid(sticky="new", padx=(12,0))
self.make_selectable(column)
curx.close()
conx.close()
def ok_export_places(self, from_column, to_column):
source = None
destiny = None
for child in from_column.winfo_children():
if child["bg"] == "black":
source = child
break
for child in to_column.winfo_children():
if child["bg"] == "black":
destiny = child
break
if source.cget("text") == destiny.cget("text"):
return
if source is None or destiny is None:
return
self.update_destination_db(source, destiny)
def update_destination_db(self, source, destiny):
source_dir = None
destiny_dir = None
source_title = source.cget("text")
destiny_title = destiny.cget("text")
for lst in self.all_trees:
if lst[0] == source_title:
source_dir = lst[1]
elif lst[0] == destiny_title:
destiny_dir = lst[1]
if source_dir == destiny_dir or source_dir is None or destiny_dir is None:
return
for idx, tree_dir in enumerate((source_dir, destiny_dir)):
# Get full_path for both trees and attach them to one connection.
if idx == 0:
source_path = f"{tree_path}/{source_dir}/{source_dir}.tbd"
conn = sqlite3.connect(source_path)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
elif idx == 1:
destiny_path = f"{tree_path}/{destiny_dir}/{destiny_dir}.tbd"
cur.execute("ATTACH ? as destiny_alias", (destiny_path,))
# Get all places, place_names, and nested_place_ids from source.
cur.execute("SELECT * FROM main.place WHERE place_id != 1")
all_source_places = [list(i) for i in cur.fetchall()]
cur.execute("SELECT * FROM main.place_name WHERE place_name_id != 1")
all_source_place_names = [list(i) for i in cur.fetchall()]
cur.execute("SELECT * FROM main.nested_place WHERE nested_place_id != 1")
all_source_nested_places = [list(i) for i in cur.fetchall()]
# Get max IDs from destiny, three tables.
# Never available: place_id 1, place_name_id 1, nested_place_id 1.
cur.execute("SELECT max(place_id) from destiny_alias.place")
max_destiny_place_id = cur.fetchone()[0]
cur.execute("SELECT max(place_name_id) from destiny_alias.place_name")
max_destiny_place_name_id = cur.fetchone()[0]
cur.execute("SELECT max(nested_place_id) from destiny_alias.nested_place")
max_destiny_nested_place_id = cur.fetchone()[0]
# Create available IDs for imported data.
for lst in all_source_places:
place_id = lst[0]
hint = self.get_place_hint(place_id, cur)
lst[0] = lst[0] + max_destiny_place_id - 1
lst.append(hint)
place_tups = [tuple(lst[0:-1]) for lst in all_source_places]
hint_tups = [tuple([lst[0], lst[-1]]) for lst in all_source_places]
for lst in all_source_place_names:
lst[0] = lst[0] + max_destiny_place_name_id - 1
lst[2] = lst[2] + max_destiny_place_id - 1
place_name_tups = [tuple(lst) for lst in all_source_place_names]
for idx, lst in enumerate(all_source_nested_places):
lst[0] = lst[0] + max_destiny_nested_place_id - 1
lst = [lst[0]] + [i + max_destiny_place_id - 1 if i != 1 else i for i in lst[1:]]
all_source_nested_places[idx] = lst
nested_place_tups = [tuple(lst) for lst in all_source_nested_places]
# Insert places to destiny, four tables.
cur.executemany(
"INSERT INTO destiny_alias.place VALUES (?, ?, ?, ?)",
place_tups)
conn.commit()
cur.executemany(
''' INSERT INTO destiny_alias.traits_tbd (place_id, hint)
VALUES (?, ?)
''',
hint_tups)
conn.commit()
cur.executemany(
"INSERT INTO destiny_alias.place_name VALUES (?, ?, ?)",
place_name_tups)
conn.commit()
cur.executemany(
''' INSERT INTO destiny_alias.nested_place
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''',
nested_place_tups)
conn.commit()
cur.execute("DETACH destiny_alias")
cur.close()
conn.close()
def get_place_hint(self, place_id, cur):
cur.execute(
''' SELECT hint
FROM traits_tbd
WHERE place_id = ?
''',
(place_id,))
hint = cur.fetchone()
if hint:
return hint[0]
else:
return "none"
def select(self, evt, children):
widg = evt.widget
for label in children:
if label == widg:
widg["bg"] = "black"
else:
label["bg"] = "#414141"
def make_selectable(self, column):
children = column.winfo_children()
for child in children:
child.bind("<Button-1>", lambda evt, children=children:
self.select(evt, children))
class EntryAutoPlace(tk.Entry):
def __init__(
self, master, tree, autofill=False, values=[], *args, **kwargs):
tk.Entry.__init__(self, master, *args, **kwargs)
self.master = master
self.tree = tree
self.autofill = autofill
self.values = values
self.tree.place_autofill_inputs.append(self)
self.autofilled = None
self.config(bd=0)
if autofill is True:
self.bind("<KeyPress>", self.detect_pressed)
self.bind("<KeyRelease>", self.get_typed)
self.bind("<FocusIn>", self.deselect, add="+")
def detect_pressed(self, evt):
""" Run on every key press. """
if self.autofill is False:
return
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)
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 = []
use_list = self.tree.prepended_places + self.tree.place_autofill_values
for item in use_list:
if item.lower().startswith(self.get().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()