treebard_root_*.py
Nov 25, 2022 18:35:13 GMT -8
Post by Uncle Buddy on Nov 25, 2022 18:35:13 GMT -8
<drive>:\treebard\treebard_root_*.py Last Changed 2024-07-25
# treebard_root_041.py
from sys import version
import tkinter as tk
from tkinter import filedialog
import sqlite3
from base import Query, app_path, unigeds_path, extension, tree_path, tbard_path
from widgets import (
StatusbarTooltips, Scrollbar, TabBook, make_formats_dict, configall)
from opening import open_tree, OpeningChoices, make_tree
from places import PlacesCopy
from persons import get_name_type_ids
from do_list import DoList
from person_maker import open_person_maker
from user_formats import get_treebard_fonts
from gedcom_import import GEDCOMImport
from gedcom_constants import IMPORT_TEXT
import dev_tools as dt
from dev_tools import look, seeline
print("python version:", version)
print("__file__:", __file__)
FILEBOOK_TABS = (
("FILES", "F"), ("SETTINGS", "S"), ("IMPORT", "I"), ("COPY", "C"),
# ("FILES", "F"), ("SETTINGS", "S"), ("IMPORT", "I"), ("EXPORT", "E"),
("SAMPLE", "X"), ("PROJECTS", "J"))
def make_widgets(formats):
scridth = 16
scridth_n = tk.Frame(treebard, height=scridth)
scridth_w = tk.Frame(treebard, width=scridth)
treebard.canvas = tk.Canvas(treebard, bd=0, highlightthickness=0)
treebard.window = tk.Frame(treebard.canvas)
treebard.canvas.create_window(0, 0, anchor='nw', window=treebard.window)
vsb = Scrollbar(
treebard,
formats,
hideable=True,
command=treebard.canvas.yview,
width=scridth)
hsb = Scrollbar(
treebard,
formats,
hideable=True,
width=scridth,
orient='horizontal',
command=treebard.canvas.xview)
treebard.canvas.config(
xscrollcommand=hsb.set,
yscrollcommand=vsb.set)
filebook = TabBook(
treebard.window, formats, dialog=treebard, takefocus=1,
tabs=FILEBOOK_TABS, miny=0.55, minx=0.50)
treebard.statusbar = StatusbarTooltips(treebard, formats)
big_button = make_open_tab(filebook, formats)
make_settings_tab(filebook, formats)
make_import_tab(filebook, formats, treebard)
make_copy_tab(filebook, formats)
make_sample_tab(filebook, formats)
make_projects_tab(filebook, formats)
# children of treebard
treebard.columnconfigure(1, weight=1)
treebard.rowconfigure(1, weight=1)
scridth_n.grid(column=0, row=0, sticky='ew')
scridth_w.grid(column=0, row=1, sticky='ns')
treebard.canvas.grid(column=1, row=1, sticky="news")
vsb.grid(column=2, row=1, sticky='ns')
hsb.grid(column=1, row=2, sticky='ew')
treebard.statusbar.grid(column=1, row=3, sticky="ew")
# children of window
treebard.window.columnconfigure(0, weight=1)
treebard.window.rowconfigure(0, weight=1)
filebook.grid(column=0, row=0, padx=12, pady=12, sticky="news")
return big_button
def make_open_tab(filebook, formats):
opening_choices = OpeningChoices(
filebook.store["FILES"], formats, treebard, filebook)
opening_choices.grid(column=0, row=0, sticky="news")
return opening_choices.big_button
def make_settings_tab(filebook, formats):
wraplength = 700
frame = tk.Frame(filebook.store["SETTINGS"])
headlab = tk.Label(
frame, text="These settings affect every part of Treebard equally. "
"For settings specific to a family tree, see the Preferences tab in "
"that tree.",
wraplength=wraplength, justify="left", bd=1, relief="raised")
text = (
"Enter a name with no letters for any persons whose name you don't "
"know, or accept the default.",
"Enter a font that's on your computer. It will be available next time "
"this tree is opened.",
)
placeholder_name = tk.Label(
frame, text=text[0], anchor="w", wraplength=350, justify="left")
placeholder_name_input = tk.Entry(frame)
query = Query()
placeholder_name_string = query.select("placeholder_name")
placeholder_name_input.insert(0, placeholder_name_string)
placeholder_name_button = tk.Button(
frame, text="SAVE PLACEHOLDER NAME",
command=lambda widg=placeholder_name_input: save_placeholder_name(widg))
fontlab = tk.Label(
frame, text=text[1], anchor="w", wraplength=350, justify="left")
add_font_input = tk.Entry(frame)
add_font_button = tk.Button(
frame, text="ADD FONT",
command=lambda widg=add_font_input: add_font(widg))
# children of filebook.store["SETTINGS"]
frame.grid(column=0, row=0, sticky="news", padx=12)
# children of frame
frame.columnconfigure(2, weight=1)
headlab.grid(column=0, row=0, pady=12, ipadx=6, ipady=6, columnspan=3)
placeholder_name.grid(column=0, row=1, sticky="w", pady=(0,12))
placeholder_name_input.grid(
column=1, row=1, padx=(6,0), sticky="w", pady=(0,12))
placeholder_name_button.grid(
column=2, row=1, padx=(6,0), sticky="w", pady=(0,12))
fontlab.grid(column=0, row=2, sticky="w", pady=(0,12))
add_font_input.grid(column=1, row=2, padx=(6,0), sticky="w", pady=(0,12))
add_font_button.grid(column=2, row=2, padx=(6,0), sticky="w", pady=(0,12))
def add_font(add_font_input):
user_font = add_font_input.get()
if len(user_font) == 0:
return
treebard_fonts = get_treebard_fonts()
conx = sqlite3.connect(tbard_path)
conx.execute("PRAGMA foreign_keys = 1")
curx = conx.cursor()
if user_font not in treebard_fonts:
curx.execute("INSERT INTO treebard_font values (?)", (user_font,))
conx.commit()
add_font_input.delete(0, "end")
def save_placeholder_name(placeholder_name_input):
name = placeholder_name_input.get()
has_letters = False
for char in name:
if char.isalpha():
has_letters = True
if has_letters or len(name) == 0:
return
placeholder_name_type_id = get_name_type_ids(
f"{tree_path}/sample_tree/sample_tree.tbd")[0]
split = name.split(" ")
sort_order = f"{split[-1]}, {' '.join(split[0:-1])}"
treebard.PLACEHOLDER_NAME = [name, sort_order, placeholder_name_type_id]
def make_import_tab(filebook, formats, treebard):
process_gedcom = ProcessGEDCOM(filebook.store["IMPORT"], treebard)
process_gedcom.grid(column=0, row=0, pady=12)
def make_copy_tab(filebook, formats):
copy_places = PlacesCopy(filebook.store["COPY"])
copy_places.grid(column=0, row=0, pady=12)
def make_sample_tab(filebook, formats):
def restore():
""" Copy...
`{app_path}/default/sample_tree_untouched.db`
as...
`{tree_path}/sample_tree/sample_tree.tbd`,
...replacing the existing file by that name if it exists.
"""
pass
sampler = tk.Button(
filebook.store["SAMPLE"], text='OPEN THE SAMPLE TREE',
command=lambda treebard=treebard, dub="Sample Tree":
open_tree(treebard, dub))
maker = tk.Button(
filebook.store["SAMPLE"],
text="MAKE A FICTIONAL PERSON",
command=lambda treebard=treebard: open_person_maker(treebard))
restorer = tk.Button(
filebook.store["SAMPLE"],
text='RESTORE THE SAMPLE TREE TO ITS ORIGINAL STATE',
command=restore)
# children of filebook.store["SAMPLE"]
sampler.grid(padx=(24,12), pady=(24,12), sticky="w")
maker.grid(padx=(24,12), pady=12, sticky="w")
restorer.grid(padx=(24,12), pady=12, sticky="w")
def make_projects_tab(filebook, formats):
text = (
f"This do list is also available in each family tree. All the to-do "
f"lists are the same. \n\nThe list you use to add a to-do item updates "
f"immediately. \n\nOther lists update when their family tree restarts, "
f"when the app restarts, or when you add/delete an item with those "
f"to-do lists.")
instrux = tk.Label(
filebook.store["PROJECTS"], text=text, wraplength=400, anchor="w",
justify="left")
do_list = DoList(filebook.store["PROJECTS"], formats, instrux)
filebook.store["PROJECTS"].columnconfigure(0, weight=1)
do_list.grid(column=0, row=0, sticky="news", padx=24, pady=24)
instrux.grid(column=1, row=0, sticky="news", padx=24, pady=24)
class ProcessGEDCOM(tk.Frame):
def __init__(self, master, treebard, *args, **kwargs):
""" Because of complications that haven't been ironed out yet, you have
to restart the app to import a second GEDCOM file. Once you press
`self.starter` to import the data, the controls are disabled,
because trying to run the code on a second file gets errors
about trying to reference destroyed widgets.
"""
tk.Frame.__init__(self, master, *args, **kwargs)
self.master = master
self.treebard = treebard
self.make_widgets()
def make_widgets(self):
def guide_user_to_next_button(evt):
""" Prevent the same button from being pushed twice. Prevent buttons
being pushed in the wrong order. Put the right button in focus
so it can be activated by pressing the spacebar.
"""
for tup in controls:
tup[1]["state"] = "disabled"
if len(tup[0]["text"]) == 0:
tup[1]["state"] = "normal"
tup[1].focus_set()
break
headlab = tk.Label(
self,
text=IMPORT_TEXT,
justify="left", wraplength=600, bd=1, relief="raised")
inputs = tk.Frame(self)
self.display_ged = tk.Label(inputs, text="", anchor="w")
self.browser = tk.Button(
inputs, text="BROWSE FOR A .ged FILE", state="disabled",
command=self.browse_for_gedcom)
self.bind("<Visibility>", guide_user_to_next_button)
self.display_tree = tk.Label(inputs, text="", anchor="w")
self.filer = tk.Button(
inputs, text="GIVE THE NEW TREE A UNIQUE NAME", state="disabled",
command=self.name_new_tree)
self.blank = tk.Label(inputs, text="", anchor="w")
self.starter = tk.Button(
inputs, text="START THE IMPORT PROCESS", state="disabled",
command=self.ok_import_gedcom)
self.resetter = tk.Button(inputs, text="START OVER", command=self.reset)
self.rowconfigure(1, weight=1)
self.columnconfigure(0, weight=1)
self.master.columnconfigure(0, weight=1)
inputs.columnconfigure(0, weight=1)
# children of self
headlab.grid(
column=0, row=0, columnspan=2, pady=12, ipadx=6, ipady=6, padx=12)
inputs.grid(column=1, row=1, sticky="news", padx=24)
# children of inputs
self.display_ged.grid(column=0, row=0, sticky="ew", pady=12)
self.browser.grid(column=1, row=0, sticky="e", pady=12, padx=12)
self.display_tree.grid(column=0, row=1, sticky="ew", pady=12)
self.filer.grid(column=1, row=1, sticky="e", pady=12, padx=12)
self.blank.grid(column=0, row=2, sticky="ew", pady=12)
self.starter.grid(column=1, row=2, sticky="e", pady=12, padx=12)
self.resetter.grid(column=1, row=3, sticky="e", pady=12, padx=12)
controls = (
(self.display_ged, self.browser), (self.display_tree, self.filer),
(self.blank, self.starter))
def reset(self):
self.source_gedcom_file = ""
self.tree_root_title = ""
if self.treebard.new_tree_id:
conx = sqlite3.connect(tbard_path)
conx.execute('PRAGMA foreign_keys = 1')
curx = conx.cursor()
curx.execute(
''' DELETE FROM family_tree
WHERE family_tree_id = ?
''',
(self.treebard.new_tree_id,))
conx.commit()
curx.close()
conx.close()
self.treebard.new_tree_id = None
for lab in self.display_ged, self.display_tree, self.blank:
lab["text"] = ""
for butt in self.browser, self.filer, self.starter:
butt["state"] = "disabled"
self.treebard.withdraw()
self.treebard.deiconify()
def browse_for_gedcom(self):
self.treebard.withdraw()
init_dir = f"{app_path}/etc"
self.source_gedcom_file = filedialog.askopenfilename(
initialdir = init_dir,
title = 'Select GEDCOM File to Open',
defaultextension = ".ged",
filetypes=(
('GEDCOM files','*.ged'),
('all files','*.*')))
if len(self.source_gedcom_file) == 0:
self.treebard.deiconify()
return
self.display_ged["text"] = self.source_gedcom_file
self.treebard.deiconify()
def name_new_tree(self):
""" `self.treebard.new_family_tree` and `self.treebard.new_tree_id`
are referenced in `gedcom_import.py`.
"""
self.treebard.withdraw()
(self.treebard.new_family_tree, self.treebard.new_tree_id,
dub) = make_tree(self.treebard, unigeds_path)
self.display_tree["text"] = dub
self.tree_root_title = dub.replace(".", "").replace(
" ", "_").lower()
self.treebard.new_family_tree.withdraw()
self.treebard.deiconify()
def ok_import_gedcom(self):
self.treebard.withdraw()
elucidom_output_path = f"{app_path}/{self.tree_root_title}_elucidom.lux"
target_unigeds_db = (
f"{tree_path}/{self.tree_root_title}/{self.tree_root_title}.tbd")
exceptions_log = (
f"{app_path}/etc/{self.tree_root_title}import_exceptions_log.txt")
GEDCOMImport(
self.treebard, elucidom_output_path, target_unigeds_db,
self.source_gedcom_file, exceptions_log)
self.blank["text"] = "Import is finished."
self.resetter["state"] = "disabled"
if __name__ == "__main__":
""" The app `treebard` is accessible globally in this module but to see it
in other modules you have to pass it as an argument in functions and
classes that are called in this module. Nothing can be imported to other
modules from this one.
"""
def exit_app():
treebard.destroy()
treebard = tk.Tk()
treebard.geometry("+75+10")
treebard.title(
"Treebard Genealogy Software Evidence--Assertion--Conclusion")
if extension in ("py", "pyc", "pyw"):
treebard.iconbitmap(default=f"{app_path}/favicon.ico")
elif extension == "exe":
ico_path = app_path.replace("/lib", "/share")
treebard.iconbitmap(default=f"{ico_path}/favicon.ico")
treebard.protocol("WM_DELETE_WINDOW", exit_app)
treebard.comboboxes = {}
treebard.maxsize(
width=treebard.winfo_screenwidth() - 12,
height=treebard.winfo_screenheight() - 36)
conn = sqlite3.connect(f"{tree_path}/sample_tree/sample_tree.tbd")
cur = conn.cursor()
conx = sqlite3.connect(tbard_path)
curx = conx.cursor()
treebard.new_tree_id = None
treebard.recent_files = []
treebard.family_trees = {}
curx.execute("SELECT family_tree_id FROM family_tree")
all_trees = [i[0] for i in curx.fetchall()]
for tree_id in all_trees:
treebard.family_trees[tree_id] = {}
treebard.family_trees[tree_id]["tree"] = None
treebard.family_trees[tree_id]["open"] = False
placeholder_name_type_id = get_name_type_ids(
f"{tree_path}/sample_tree/sample_tree.tbd")[0]
treebard.PLACEHOLDER_NAME = [
"_____ _____", "_____, _____", placeholder_name_type_id]
cur.execute('PRAGMA table_list')
treebard.TYPES = sorted([
i[1] for i in cur.fetchall() if i[0] == "main" and i[1].endswith("_type")])
treebard.formats = make_formats_dict(None)
treebard.big_button = make_widgets(treebard.formats)
# Do this every toplevel window*********
configall(treebard, treebard.formats)
# **************************************
TabBook.resize_scrolled_dialog_with_tabbook(
treebard, treebard.canvas, treebard.window)
treebard.focus_force()
treebard.update_idletasks()
treebard.big_button.focus_set()
curx.close()
conx.close()
cur.close()
conn.close()
treebard.mainloop()