notes.py
Nov 25, 2022 3:54:58 GMT -8
Post by Uncle Buddy on Nov 25, 2022 3:54:58 GMT -8
<drive>:\treebard\app\python\notes.py Last Changed 2024-03-21
# notes.py
import tkinter as tk
import sqlite3
from files import get_current_tree, appwide_db_path
from utilities import center_dialog, OK_PRINT_KEYS, resize_scrolled_content
from widgets import (
create_tooltip, run_statusbar_tooltips, Scrollbar, ScrolledText, LabelMovable,
LabelHover, configall, make_formats_dict, get_colors_type_id, ScrolledDialog,
RightClickMenu, make_rc_menus)
from messages_context_help import notes_dlg_help_msg, links_dlg_help_msg
from messages import notes_msg
from dates import ChangeDate
from query_strings import (
insert_note, update_note_privacy, select_note_privacy, select_note_id,
update_note_edit, update_notes_links_note_order_event,
insert_notes_links_note_event, update_notes_links_note_order,
select_notes_per_event, select_notes_links_events,
delete_notes_links_unlink_note, update_note_topic_event,
update_note_topic_assertion, select_notes_per_assertion,
select_notes_links_note_order_event,
insert_notes_links_note_assertion, select_notes_links_assertions,
update_notes_links_note_order_assertion,
select_notes_links_note_order_assertion, select_notes_per_place,
insert_notes_links_note_place, select_notes_links_places,
update_note_topic_place, select_notes_links_note_order_place,
update_notes_links_note_order_place, select_notes_ids_by_text_event,
select_notes_ids_by_text_place, select_notes_ids_by_text_assertion,
select_notes_links_notes_available_assertion,
select_notes_links_notes_available_event,
select_notes_links_notes_available_place)
import dev_tools as dt
from dev_tools import look, seeline
class NotesDialog(ScrolledDialog):
""" `toc` means Table of Contents. """
def __init__(
self,
master,
family_tree_id,
treebard,
header=None,
inwidg=None,
current_person_id=None,
event_id=None,
assertion_id=None,
place_id=None,
linked_element=None,
*args, **kwargs):
ScrolledDialog.__init__(self, master, *args, **kwargs)
self.family_tree = master
self.family_tree_id = family_tree_id
self.treebard = treebard
self.header = header
self.inwidg = inwidg
self.current_person_id = current_person_id
self.event_id = event_id
self.assertion_id = assertion_id
self.place_id = place_id
self.linked_element = linked_element
if self.linked_element == "place":
self.place_id, self.place_name = self.place_id[0:2]
self.default_title = "Notes Dialog"
self.title(self.default_title)
conn = sqlite3.connect(appwide_db_path)
cur = conn.cursor()
self.current_tree = get_current_tree(self.family_tree_id, cur)[0]
cur.execute("ATTACH ? as tree", (self.current_tree,))
# self.selected won't be None after a label is selected, regardless
# of whether or not a label is still highlighted.
self.selected = None
self.topics = []
self.notes = []
self.privacy = tk.IntVar()
self.rc_menu = RightClickMenu(self.family_tree, self.treebard)
self.autoscroll_on_arrow = False
self.make_widgets()
self.make_inputs()
self.define_queries_by_link()
self.make_table_of_contents(cur)
self.size_toc()
self.resize_scrolled_content(self, self.canvas, add_x=16, add_y=24)
center_dialog(self, frame=self.window)
self.bind_class('Label', '<Tab>', self.stop_tab_traversal1)
self.bind_class('Label', '<Shift-Tab>', self.stop_tab_traversal2)
for widg in (self.topic_input, self.order, self.public,
self.private, self.close_button):
widg.bind("<Key-Up>", self.focus_topicslist)
widg.bind("<Key-Down>", self.focus_topicslist)
self.bind('<Escape>', self.close_notes_dialog)
ScrolledDialog.bind_canvas_to_mousewheel(self.canvas)
colors_type_id = get_colors_type_id(self.family_tree_id)
self.formats = make_formats_dict(colors_type_id=colors_type_id)
configall(self, self.formats)
def make_widgets(self):
if self.linked_element == "event":
name = self.family_tree.person_autofill_values[
self.current_person_id][0]["name"]
self.title(
f"{self.default_title} Current Person: {name}, ID #{self.current_person_id}")
elif self.linked_element == "assertion":
self.title(f"{self.default_title} Current event: event ID #{self.event_id}")
elif self.linked_element == "place":
self.title(f"{self.default_title} Place: {self.place_name}")
self.frame = tk.Frame(self.window)
# make table of contents scrollable
self.toc_window = tk.Frame(self.frame, bd=2, relief='sunken')
self.toc_canvas = tk.Canvas(self.toc_window, bd=0, highlightthickness=0)
self.toc = tk.Frame(self.toc_canvas)
self.toc_canvas.create_window(0, 0, anchor='nw', window=self.toc)
self.toc_scrollbar = Scrollbar(
self.toc_window, self.formats, command=self.toc_canvas.yview,
hideable=False)
self.toc_canvas.config(yscrollcommand=self.toc_scrollbar.set)
def make_inputs(self):
""" Height of text widget can't be less than 14. """
self.header_msg = tk.Label(
self.frame, text=self.header, justify="left", bd=1, relief="raised")
self.toc_head = tk.Label(
self.frame, text="TABLE OF CONTENTS", anchor="w")
self.topic_input = tk.Entry(self.frame, width=36)
self.note = ScrolledText(self.frame, self.formats)
self.note.text.config(height=14, width=56)
self.note.text.selected_note_text = None
SuggestionsPopUp(
self.family_tree, self.family_tree_id, self, self.topic_input,
self.note.text, self.linked_element)
self.controls = tk.Frame(self.frame)
self.order = tk.Button(
self.controls,
text='CHANGE TOPIC ORDER',
command=self.reorder_notes)
radframe = tk.LabelFrame(
self.frame, text='Make selected note...', takefocus=1)
self.public = tk.Radiobutton(
radframe,
text='...public',
anchor='w',
variable=self.privacy,
value=0,
command=self.save_privacy_setting)
self.private = tk.Radiobutton(
radframe,
text='...private',
anchor='w',
variable=self.privacy,
value=1,
command=self.save_privacy_setting)
radframe.bind("<Enter>", self.disable_radios)
radframe.bind("<Leave>", self.enable_radios)
radframe.bind("<FocusIn>", self.disable_radios)
radframe.bind("<FocusOut>", self.enable_radios)
self.buttons = tk.Frame(self.frame)
self.ok_button = tk.Button(
self.buttons, text="SAVE NEW TOPIC & NOTE",
command=self.make_note)
self.close_button = tk.Button(
self.buttons, text='CLOSE',
command=self.close_notes_dialog)
# children of self.window
self.frame.grid(column=0, row=0, sticky="news")
# children of self.frame
self.frame.columnconfigure(0, weight=1)
self.frame.rowconfigure(2, weight=1)
self.header_msg.grid(
column=0, row=0, columnspan=3,
ipadx=9, ipady=9, pady=(0,12), sticky="w")
self.toc_head.grid(column=0, row=1, sticky="ew", pady=6)
self.toc_window.grid(column=0, row=2, sticky="ew")
self.controls.grid(column=0, row=3, sticky="sw")
self.topic_input.grid(
column=1, row=1, sticky="w", pady=6, padx=(6,0), columnspan=2)
self.topic_input.selected_note = None
self.note.grid(column=1, row=2, columnspan=2, padx=(6,0), ipady=2)
radframe.grid(column=1, row=3, columnspan=2, sticky="w", padx=(6,0), pady=(6,12))
self.buttons.grid(column=2, row=3, sticky="se", pady=(6,12))
# children of self.toc_window
self.toc_canvas.grid(column=0, row=0, sticky="news", rowspan=2)
self.toc_scrollbar.grid(column=1, row=1, sticky="ns")
# children of self.controls
self.controls.rowconfigure(0, weight=1)
self.order.grid(column=0, row=1, sticky='sw', pady=(6,12))
# children of radframe
self.public.grid(column=0, row=0, sticky='news', padx=12)
self.private.grid(column=1, row=0, sticky='news', padx=12)
# children of self.buttons
self.ok_button.grid(column=0, row=0, sticky="e", padx=(0,6))
self.close_button.grid(column=1, row=0, sticky='e')
visited = (
(self.public,
'Public Note Option',
'Public notes will be shared when sharing tree.'),
(self.private,
'Private Note Option',
"Private notes stay on the tree creator's copy of Treebard."),
(self.order,
'Reorder Topics Button',
'Press to reorder topics in table of contents.'),
(self.note.text,
'Note Input & Readout',
'Create and edit notes of any length.'),
(self.topic_input,
'Note Topic Input',
'Select, create, or edit a note topic.'),
)
run_statusbar_tooltips(
visited, self.statusbar.status_label,
self.statusbar.tooltip_label, self)
rcm_widgets = (
self.topic_input, self.note.text, self.toc_head,
radframe, self.order)
make_rc_menus(
rcm_widgets,
self.rc_menu,
notes_dlg_help_msg)
self.topic_input.focus_set()
self.topic_input.bind("<FocusIn>", self.display_note)
self.topic_input.bind("<KeyRelease>", self.display_note)
def focus_topicslist(self, evt):
if self.length < 1:
return
last = self.topiclabs[self.length - 1]
first = self.topiclabs[0]
sym = evt.keysym
if sym == "Up":
last.focus_set()
self.selected = last
self.toc_canvas.yview_moveto(1.0)
elif sym == "Down":
first.focus_set()
self.selected = first
self.toc_canvas.yview_moveto(0.0)
self.display_note()
def ignore_changes(self, evt=None):
self.order_dlg.grab_release()
self.order_dlg.destroy()
self.focus_set()
def reorder_notes(self):
if self.length <= 1:
return
self.order_dlg = ScrolledDialog(self)
self.order_dlg.columnconfigure(1, weight=1)
self.order_dlg.grab_set()
self.order_dlg.bind('<Return>', self.save_close_reorder_dlg)
self.order_dlg.bind('<Escape>', self.ignore_changes)
self.order_dlg.title("Re-order Topics")
self.make_inputs_reorder_dlg()
colors_type_id = get_colors_type_id(family_tree_id)
formats = make_formats_dict(colors_type_id=colors_type_id)
configall(self.order_dlg, formats)
self.order_dlg.maxsize(
int(self.order_dlg_window.winfo_reqwidth() + 24),
int(self.winfo_screenheight() * 0.80))
resize_scrolled_content(
self.order_dlg, self.order_dlg.canvas, self.order_dlg.window)
def make_inputs_reorder_dlg(self):
reorder_msg = (
'Tab or Shift + Tab selects movable topic. '
'Arrow keys change topic order up or down.')
reorder_lab = tk.Label(
self.order_dlg_window,
text=reorder_msg, wraplength=450, bd=1, relief="raised")
self.reorder_labs = tk.Frame(self.order_dlg_window)
e = 0
for topic in self.topics:
lab = LabelMovable(
self.reorder_labs,
text=topic, anchor='w')
if e == 0:
first = lab
e += 1
lab.grid(column=0, row=e, padx=3, sticky='ew')
first.focus_set()
close2 = tk.Button(
self.order_dlg_window, text='OK',
command=self.save_close_reorder_dlg)
reorder_lab.grid(
column=0, row=0, pady=(12,0), padx=12,
columnspan=2, ipadx=6, ipady=3)
self.reorder_labs.grid(column=0, row=1, columnspan=2, padx=12, pady=12)
self.reorder_labs.grid_columnconfigure(0, weight=1)
close2.grid(column=1, row=2, sticky='se', padx=12, pady=(0,12))
center_dialog(self.order_dlg, frame=self.order_dlg_window)
def save_close_reorder_dlg(self, evt=None):
q = 0
new_order = []
save_order = []
for child in self.reorder_labs.winfo_children():
text = child.cget('text')
new_order.append(text)
save_order.append([text, q])
q += 1
conn = sqlite3.connect(appwide_db_path)
conn.execute('PRAGMA foreign_keys = 1')
cur = conn.cursor()
cur.execute("ATTACH ? as tree", (self.current_tree,))
for lst in save_order:
cur.execute(select_note_id, (lst[0],))
result = cur.fetchone()
if result:
note_id = result[0]
else:
continue
cur.execute(
self.update_note_order,
(lst[1], self.element_id, note_id))
conn.commit()
self.make_table_of_contents(cur)
cur.execute("DETACH tree")
cur.close()
conn.close()
self.order_dlg.grab_release()
self.order_dlg.destroy()
self.focus_set()
def save_privacy_setting(self):
selected_topic = self.selected.cget("text")
conn = sqlite3.connect(self.current_tree)
conn.execute('PRAGMA foreign_keys = 1')
cur = conn.cursor()
for idx,topic in enumerate(self.topics):
if topic == selected_topic:
idnum = self.ids[idx]
break
cur.execute(update_note_privacy, (self.privacy.get(), idnum))
conn.commit()
ChangeDate(idnum, tag="note", conn=conn, cur=cur)
cur.close()
conn.close()
def close_notes_dialog(self, evt=None):
self.destroy()
self.treebard.focus_set()
def size_toc(self):
self.update_idletasks()
self.win_ht = self.toc.winfo_reqheight()
self.canv_ht = self.note.winfo_reqheight() - self.toc_window["bd"] * 2
self.toc_canvas.config(
width=self.toc_width,
height=self.canv_ht,
scrollregion=(0, 0, self.toc_width, self.win_ht))
unzero = self.length
if unzero == 0:
unzero = 1
self.widg_ht = int(self.win_ht / unzero)
lines_fit = int(self.canv_ht / self.widg_ht)
if self.length > lines_fit:
self.autoscroll_on_arrow = True
def stop_tab_traversal1(self, evt):
""" Two similar callbacks are needed for binding to both `Tab` and
`Shift-Tab` because Tkinter's `evt.keysym` detects `Tab` for both.
"""
widg = evt.widget
if widg not in self.topiclabs:
return
self.topic_input.focus_set()
return('break')
def stop_tab_traversal2(self, evt):
widg = evt.widget
if widg not in self.topiclabs:
return
self.done.focus_set()
return('break')
def select_item(self, evt, next_item=None, prev_item=None):
for widg in self.topiclabs:
widg.config(bg=self.formats['bg'])
sym = evt.keysym
evt_type = evt.type
if evt_type == '4':
self.selected = evt.widget
self.display_note()
elif evt_type == '2' and sym == 'Down':
self.selected = next_item
elif evt_type == '2' and sym == 'Up':
self.selected = prev_item
self.selected.config(bg=self.formats['highlight_bg'])
self.update_idletasks()
current_scrollratio = 0.0
top_of_selected = self.selected.winfo_rooty()
top_of_port = self.toc_canvas.winfo_rooty()
height_of_selected = self.selected.winfo_reqheight()
bottom_of_selected = top_of_selected + height_of_selected
bottom_of_port = top_of_port + self.canv_ht
page_ratio = self.canv_ht / self.win_ht
# autoscroll in 1-page increments during arrow traversal
# if selected widget goes out of view
if bottom_of_selected > bottom_of_port:
current_scrollratio += page_ratio
self.toc_canvas.yview_moveto(float(current_scrollratio))
elif top_of_selected < top_of_port:
if self.autoscroll_on_arrow is True:
current_scrollratio -= page_ratio
self.toc_canvas.yview_moveto(float(current_scrollratio))
self.selected.focus_set()
def traverse_on_arrow(self, evt):
widg = evt.widget
sym = evt.keysym
self.update_idletasks()
next_item = widg.tk_focusNext()
prev_item = widg.tk_focusPrev()
if sym == 'Down':
if next_item in self.topiclabs:
self.select_item(evt, next_item=next_item)
else:
next_item = self.topiclabs[0]
next_item.focus_set()
next_item.config(bg=self.formats['highlight_bg'])
self.selected = next_item
self.toc_canvas.yview_moveto(0.0)
elif sym == 'Up':
if prev_item in self.topiclabs:
self.select_item(evt, prev_item=prev_item)
else:
prev_item = self.topiclabs[self.length-1]
prev_item.focus_set()
prev_item.config(bg=self.formats['highlight_bg'])
self.selected = prev_item
if self.autoscroll_on_arrow is True:
self.toc_canvas.yview_moveto(1.0)
self.display_note()
def make_table_of_contents(self, cur=None):
def highlight(evt):
evt.widget["bg"] = self.formats["highlight_bg"]
def unhighlight(evt):
evt.widget["bg"] = self.formats["bg"]
for child in self.toc.winfo_children():
child.destroy()
new_connex = False
if cur is None:
conn = sqlite3.connect(appwide_db_path)
cur = conn.cursor()
new_connex = True
cur.execute("ATTACH ? as tree", (self.current_tree,))
cur.execute(self.select_notes_query, (self.element_id,))
values = cur.fetchall()
if new_connex:
cur.execute("DETACH tree")
cur.close()
conn.close()
self.topics = [i[0] for i in values]
self.notes = [i[1] for i in values]
self.ids = [i[2] for i in values]
self.update_idletasks()
self.toc_width = self.order.winfo_reqwidth() + 9
self.topiclabs = []
for idx,tup in enumerate(values):
text = values[idx][0]
lab = LabelHover(
self.toc,
takefocus=1, anchor="w", text=text, width=self.toc_width)
self.topiclabs.append(lab)
lab.grid(column=0, row=idx+1, sticky="ew")
lab.bind("<Button-1>", self.select_item)
lab.bind("<Key-Up>", self.traverse_on_arrow, add="+")
lab.bind("<Key-Down>", self.traverse_on_arrow, add="+")
lab.bind("<Delete>", self.unlink_selected_note)
lab.bind("<BackSpace>", self.rename_selected_note)
lab.bind("<FocusIn>", highlight)
lab.bind("<FocusOut>", unhighlight)
if len(text) > 17:
create_tooltip(lab, text)
self.length = len(self.topiclabs)
configall(self.toc, self.formats)
def rename_selected_note(self, evt):
widg = evt.widget
old_txt = widg.cget("text")
msg = tk.Toplevel(self)
msg.title("Rename Note")
lab1 = tk.Label(
msg, text=f"Rename {old_txt} to:")
lab1.grid(column=0, row=0, sticky="news")
lab2 = tk.Label(msg, text="(any unique title)", anchor="w")
lab2.grid()
ent = tk.Entry(msg)
ent.grid()
msg.grab_set()
if msg.ok_was_pressed:
new_txt = msg.show()
conn = sqlite3.connect(self.current_tree)
conn.execute('PRAGMA foreign_keys = 1')
cur = conn.cursor()
for idx, topic in enumerate(self.topics):
if old_txt == topic:
idnum = self.ids[idx]
break
cur.execute(self.update_topic_query, (new_txt, idnum, self.element_id))
conn.commit()
ChangeDate(idnum, tag="note", conn=conn, cur=cur)
self.make_table_of_contents(cur)
cur.close()
conn.close()
def unlink_selected_note(self, evt):
def ok_unlink_note():
msg.grab_release()
msg.destroy()
self.topic_input.focus_set()
proceed()
def cancel_unlink_note():
msg.grab_release()
msg.destroy()
widg.focus_set()
def proceed():
conn = sqlite3.connect(self.current_tree)
conn.execute('PRAGMA foreign_keys = 1')
cur = conn.cursor()
cur.execute(self.select_notes_by_topic, (topic, self.element_id))
to_unlink = cur.fetchone()
for num in to_unlink:
cur.execute(delete_notes_links_unlink_note, (num,))
conn.commit()
# recalculate scrollregion height
win_ht_new = self.win_ht - widg_ht
self.toc_canvas.config(
width=self.toc_width,
height=self.canv_ht,
scrollregion=(0, 0, self.toc_width, win_ht_new))
self.topic_input.delete(0, "end")
self.make_table_of_contents(cur)
if len(self.topics) == 0:
self.inwidg.config(text=" ")
cur.close()
conn.close()
self.protocol("WM_DELETE_WINDOW", cancel_unlink_note)
widg = evt.widget
topic = widg.cget('text')
widg_ht = widg.winfo_reqheight()
msg = tk.Toplevel(self)
msg.title("Unlink Note Confirmation")
msg.grab_set()
lab = tk.Label(
msg, text=notes_msg[5], justify='left', wraplength=600,
bd=1, relief="raised")
buttons = tk.Frame(msg)
ok_butt = tk.Button(
buttons, text="OK", command=ok_unlink_note, width=6)
cancel_butt = tk.Button(
buttons, text="CANCEL", command=cancel_unlink_note, width=6)
lab.grid(
column=0, row=0, sticky='news', padx=12, pady=12,
columnspan=2, ipadx=6, ipady=3)
buttons.grid(column=0, row=1, sticky='e', padx=(0,12), pady=12)
ok_butt.grid(column=0, row=0, padx=6)
cancel_butt.grid(column=1, row=0, padx=6)
ok_butt.focus_set()
formats = make_formats_dict(colors_type_id=1)
configall(msg, formats)
def make_note(self):
""" Tkinter automatically adds a newline character at the end of the
data in the Text widget, so to detect an empty Text widget, use
get(1.0, 'end-1c') instead of get(1.0, 'end'). This gets the text
up to 1 character from the end.
"""
topic = self.topic_input.get()
note = self.note.text.get(1.0, 'end-1c')
if (len(note) == 0 or len(topic) == 0):
return
if topic in self.topics:
idx = self.topics.index(topic)
self.edit_note(idx, note)
return
if len(self.topiclabs) != 0:
self.topiclabs[0].focus_set()
self.save_note(topic, note)
self.make_table_of_contents()
self.size_toc()
self.resize_scrolled_content(self, self.canvas, add_x=16, add_y=24)
def edit_note(self, idx, note):
def save_edited_text():
conn = sqlite3.connect(self.current_tree)
conn.execute('PRAGMA foreign_keys = 1')
cur = conn.cursor()
cur.execute(update_note_edit, (final, idnum))
conn.commit()
ChangeDate(idnum, tag="note", conn=conn, cur=cur)
cur.close()
conn.close()
self.make_table_of_contents()
orig = self.notes[idx]
idnum = self.ids[idx]
final = note
if orig == final:
pass
else:
save_edited_text()
def save_note(self, topic, note):
def reorder_notes():
cur.execute(self.select_note_order_query, (self.element_id,))
order = [list(i) for i in cur.fetchall()]
for lst in order:
lst[0] += 1
for lst in order:
cur.execute(update_notes_links_note_order, tuple(lst))
conn.commit()
conn = sqlite3.connect(self.current_tree)
conn.execute('PRAGMA foreign_keys = 1')
cur = conn.cursor()
if self.topic_input.selected_note is None:
cur.execute(insert_note, (note,))
conn.commit()
note_id = cur.lastrowid
else:
note_id = self.topic_input.selected_note
self.topic_input.selected_note = None
self.note.text.selected_note_text = None
if len(self.topics) == 0:
self.inwidg.config(text=" ... ")
reorder_notes()
cur.execute(
self.insert_notes_links_new_note, (
self.element_id, note_id, self.length, topic))
ChangeDate(note_id, tag="note", conn=conn, cur=cur)
conn.commit()
cur.close()
conn.close()
def display_note(self, evt=None):
def do_it(selected_topic):
if selected_topic is None:
self.note.text.delete(1.0, 'end')
return
idx = self.topics.index(selected_topic)
note_to_display = self.notes[idx]
self.topic_input.delete(0, 'end')
self.topic_input.insert(0, selected_topic)
self.note.text.delete(1.0, 'end')
self.note.text.insert(1.0, note_to_display)
self.selected = self.topiclabs[idx]
self.selected.focus_set()
conn = sqlite3.connect(self.current_tree)
cur = conn.cursor()
cur.execute(select_note_privacy, (selected_topic,))
privacy_setting = cur.fetchone()[0]
cur.close()
conn.close()
if privacy_setting == 0:
self.public.select()
elif privacy_setting == 1:
self.private.select()
selected_topic = None
if evt is None:
selected_topic = self.selected.cget("text")
do_it(selected_topic)
return
etype = evt.type
widg = evt.widget
sym = evt.keysym
if etype == "4": # Button-1
selected_topic = widg.cget('text')
elif etype == "9": # FocusIn
if len(widg.get()) == 0:
pass
else:
selected_topic = widg.get().strip()
elif etype == "3": # KeyRelease
if widg is self.topic_input:
if (len(sym) == 1 or
sym in OK_PRINT_KEYS or
sym in ("BackSpace", "Delete")):
selected_topic = self.topic_input.get()
if selected_topic in self.topics:
do_it(selected_topic)
else:
self.note.text.delete(1.0, 'end')
def define_queries_by_link(self):
if self.linked_element == "assertion":
self.select_notes_query = select_notes_per_assertion
self.select_notes_ids_by_text = select_notes_ids_by_text_assertion
self.insert_notes_links_new_note = insert_notes_links_note_assertion
self.select_notes_by_topic = select_notes_links_assertions
self.update_topic_query = update_note_topic_assertion
self.select_note_order_query = select_notes_links_note_order_assertion
self.update_note_order = update_notes_links_note_order_assertion
self.element_id = self.assertion_id
elif self.linked_element == "event":
self.select_notes_query = select_notes_per_event
self.select_notes_ids_by_text = select_notes_ids_by_text_event
self.insert_notes_links_new_note = insert_notes_links_note_event
self.select_notes_by_topic = select_notes_links_events
self.update_topic_query = update_note_topic_event
self.select_note_order_query = select_notes_links_note_order_event
self.update_note_order = update_notes_links_note_order_event
self.element_id = self.event_id
elif self.linked_element == "place":
self.select_notes_query = select_notes_per_place
self.select_notes_ids_by_text = select_notes_ids_by_text_place
self.insert_notes_links_new_note = insert_notes_links_note_place
self.select_notes_by_topic = select_notes_links_places
self.update_topic_query = update_note_topic_place
self.select_note_order_query = select_notes_links_note_order_place
self.update_note_order = update_notes_links_note_order_place
self.element_id = self.place_id
def disable_radios(self, evt):
if self.selected is None:
for radio in (self.public, self.private):
radio.config(state="disabled")
def enable_radios(self, evt):
for radio in (self.public, self.private):
radio.config(state="normal")
class SuggestionsPopUp(ScrolledDialog):
def __init__(
self, master, family_tree_id, note_dialog_instance, inwidg, textbox,
linked_element, *args, **kwargs):
ScrolledDialog.__init__(self, master, *args, **kwargs)
self.family_tree = master
self.family_tree_id = family_tree_id
self.note_dialog_instance = note_dialog_instance
self.inwidg = inwidg
self.textbox = textbox
self.linked_element = linked_element
self.canvas.config(bd=1, highlightthickness=1)
for widg in self.scridth_n, self.scridth_w:
widg.grid_forget()
self.withdraw()
self.screen_height = self.winfo_screenheight()
self.fit_height = int(self.screen_height/3)
self.notes = {}
self.maxsize(
width=int(self.winfo_screenwidth() * 0.90),
height=self.fit_height)
self.make_widgets()
self.define_queries_by_link()
self.bind("<Escape>", self.close)
self.inwidg.bind("<FocusOut>", self.show_popup)
def make_widgets(self):
self.columnconfigure(0, weight=1)
id_head = tk.Label(self.window, text="ID", anchor="w")
topic_head = tk.Label(self.window, text="TOPIC", anchor="w")
note_head = tk.Label(self.window, text = "NOTE (truncated)", anchor="w")
id_head.grid(column=0, row=0, sticky="ew")
topic_head.grid(column=1, row=0, sticky="ew")
note_head.grid(column=2, row=0, sticky="ew")
def make_inputs(self):
conn = sqlite3.connect(appwide_db_path)
cur = conn.cursor()
current_tree = get_current_tree(self.family_tree_id, cur)[0]
cur.execute("ATTACH ? as tree", (current_tree,))
cur.execute(
self.select_available_topics_query,
(self.note_dialog_instance.element_id,))
results = [list(i) for i in cur.fetchall()]
linked = []
unlinked = []
for lst in results:
if lst[1] is None:
unlinked.append((lst[0], "none", lst[2]))
else:
linked.append(lst)
list1 = sorted(linked, key=lambda i: i[1].lower())
list2 = sorted(unlinked, key=lambda i: i[1])
self.available_notes = list1 + list2
for tup in self.available_notes:
topic = tup[1]
self.notes[tup[0]] = {"topic": topic, "note": tup[2]}
for row, (key, val) in enumerate(self.notes.items(), 1):
idnum = key
topic = val["topic"]
for k,v in val.items():
if topic:
topic = topic
else:
topic = "none"
note = val["note"]
self.make_table_row(idnum, topic, note, row)
cur.execute("DETACH tree")
cur.close()
conn.close()
def define_queries_by_link(self):
if self.linked_element == "assertion":
self.select_available_topics_query = select_notes_links_notes_available_assertion
elif self.linked_element == "event":
self.select_available_topics_query = select_notes_links_notes_available_event
elif self.linked_element == "place":
self.select_available_topics_query = select_notes_links_notes_available_place
def make_table_row(self, idnum, topic, note, row):
def highlight(evt):
evt.widget.config(cursor="hand2")
def unhighlight(evt):
evt.widget.config(cursor="arrow")
length = len(note)
if length == 0:
note = "..."
elif length > 48:
note = f"{note[0:49]}..."
else:
note = note
lab1 = tk.Label(self.window, text=idnum, anchor="w")
lab2 = tk.Label(self.window, text=topic[0:12], anchor="w")
lab3 = tk.Label(
self.window, text=note, anchor="w", bd=1,
wraplength=180, justify="left")
lab1.grid(column=0, row=row, sticky="nw", padx=(0,6))
lab2.grid(column=1, row=row, sticky="nw", padx=(0,6))
lab3.grid(column=2, row=row, sticky="ew", padx=(0,6))
for widg in lab1, lab2, lab3:
widg.bind("<Button-1>", self.select)
widg.bind("<Enter>", highlight)
widg.bind("<Leave>", unhighlight)
def show_popup(self, evt):
""" On FocusOut of topic input, if an available new topic is in the
input, open a list of available notes to select from. Sort the
selections alphabetically by the topic.
"""
new_topic = evt.widget.get().strip()
if new_topic in self.note_dialog_instance.topics or len(new_topic) == 0:
return
self.new_topic = new_topic
self.make_inputs()
colors_type_id = get_colors_type_id(self.family_tree_id)
formats = make_formats_dict(colors_type_id=colors_type_id)
configall(self, formats)
self.resize_scrolled_content(self, self.canvas)
self.position_popup()
def position_popup(self):
self.update_idletasks()
x = self.inwidg.winfo_rootx() + 1 # add for border width
y = self.inwidg.winfo_rooty()
entry_height = self.inwidg.winfo_reqheight()
self.wm_overrideredirect(1)
fly_up = self.get_vertical_pos(entry_height)
if fly_up[0] is False:
y = y + entry_height
else:
y = fly_up[1]
self.geometry(f"{self.window.winfo_reqwidth() + 24}x{self.fit_height}+{x}+{y}")
self.deiconify()
self.focus_set()
def get_vertical_pos(self, entry_height):
fly_up = False
vert_pos = self.inwidg.winfo_rooty()
clearance = self.screen_height - (vert_pos + entry_height)
if clearance < self.fit_height:
fly_up = True
return (fly_up, vert_pos - self.fit_height)
def select(self, evt):
widg = evt.widget
idx = widg.grid_info()['row'] - 1
self.inwidg.selected_note = list(self.notes.keys())[idx]
self.textbox.selected_note_text = self.notes[self.inwidg.selected_note]["note"]
self.close()
def close(self, evt=None):
for child in self.window.winfo_children():
# if child.style != "labelh3":
# child.destroy()
if self.textbox.selected_note_text:
self.textbox.delete(1.0, 'end')
self.textbox.insert(1.0, self.textbox.selected_note_text)
self.withdraw()
self.notes = {}