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_gps\app\python\treebard_root_*.py Last Changed 2024-04-16
# treebard_root_036.py
""" Events table and colorizer were rewritten. LabelEntryDynamic & its
overlay edit widget were replaced with Cell, CellAutofill, CellAutoEntryType,
CellAutoPlace. The `screen` came into existence to hide flashing widgets
during load and on changing the current person. Hiding the flashing widgets
unexpectedly reduced process time to a third or fourth of the previous
duration because, not only are the flashing widgets not seen, the screen
prevents them from being displayed at all until the formatting and resizing
are complete. Overlays are still used in the families table and roles dialog.
"""
from shutil import copytree
import tkinter as tk
from tkinter import filedialog
import sqlite3
from PIL import Image, ImageTk
from files import app_path, appwide_db_path, current_drive, default_new_tree
from widgets import (
StatusbarTooltips, Scrollbar, TabBook, make_formats_dict, configall,
NEUTRAL_COLOR)
from opening import (
open_tree, OpeningChoices, exit_app, get_backups_folder, make_tree)
from assertions import delete_current_source
from places import PlacesExport
from person_maker import open_person_maker
from messages import treebard_msg
from query_strings import (
update_family_tree_all_trees_closed, update_preference_combo_scroll_width,
select_all_family_tree_ids)
from gedcom_import import GEDCOMImport
from gedcom_constants import IMPORT_TEXT
import dev_tools as dt
from dev_tools import look, seeline
""" IMPORT STRUCTURE (not up to date)
utilities
|
files
|
widgets
____________________________________|________________________
| | | |
media do_list dates___________ redraw
| | _________________________|____________ | |
| | | | | | | | |_____________
| | gallery graphics notes | persons | | | |
|_____| | | | | | | | | | | | |
|_____|_______| | | | | | | | | | |
| assertions places|roles| families | colorizer places
| | | | | | | | |
| | | | | | | | |
| | persons |_____search___|__| | |
| | | | | | |
| | events_table | | | |
|______|______|_________________|_____|_________| |
| | |
| main |
| _______|______ |
| | | |
| person_maker opening |
|_______| |_____________________________|
| |
treebard_root_*
"""
# # I don't remember what became of this effort except that the warn_too_late caused a problem and I got rid of it. Had something to do with opening a separate Tk instance. Start over from scratch. The problem is that recycle bin is not available as Windows users will expect it to be. Deleting a file with code sends it away forever. We need a place to send things that the user deletes with a Python procedure so he can change his mind and get it back later.
# backups_folder_error = "Treebard can't open unless a valid backups directory is chosen. This directory can be on any drive and the directory has to be outside the directory called 'treebard' where your copy of Treebard was saved on your computer. The location of the backups folder can be changed at any time from the SETTINGS tab. Treebard is a portable application, and the backups folder takes the place of Windows Recycle Bin in case you want to restore a tree after it has been deleted. Try opening Treebard again."
FILEBOOK_TABS = (
("FILES", "F"), ("SETTINGS", "S"), ("IMPORT", "I"), ("EXPORT", "E"),
("SAMPLE", "X"))
def placeholder(evt=None, name=""):
print('menu test:', name.upper())
print('evt:', evt)
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_export_tab(filebook, formats)
make_sample_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")
TabBook.resize_scrolled_dialog_with_tabbook(
treebard, treebard.canvas, treebard.window)
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):
def set_combobox_scrollbar_width():
width = comboscrollvar.get()
conn = sqlite3.connect(appwide_db_path)
conn.execute('PRAGMA foreign_keys = 1')
cur = conn.cursor()
cur.execute(update_preference_combo_scroll_width, (width,))
conn.commit()
cur.close()
conn.close()
wraplength = 700
comboscrollvar = tk.StringVar(None, "fat")
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")
lframe_combo = tk.LabelFrame(frame, text="COMBOBOX SCROLLBAR WIDTH")
spacer = tk.Frame(lframe_combo)
spacer.pack(side="left", fill="x", expand="1")
for idx,text in enumerate(("skinny", "fat")):
rad = tk.Radiobutton(
lframe_combo, text=text, value=text, variable=comboscrollvar,
anchor="e")
rad.pack(side="left", fill="x", padx=(0,12))
ok_butt = tk.Button(
lframe_combo, text="OK", width=5, command=set_combobox_scrollbar_width)
# children of filebook.store["SETTINGS"]
frame.grid(column=0, row=0, sticky="news")
# children of frame
headlab.grid(column=0, row=0, padx=12, pady=12, ipadx=6, ipady=6)
lframe_combo.grid(column=0, row=1, padx=12, pady=12)
# children of lframe_combo
ok_butt.pack(side="right", pady=12, padx=(0,12))
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_export_tab(filebook, formats):
export_places = PlacesExport(filebook.store["EXPORT"])
export_places.grid(column=0, row=0, pady=12)
def make_sample_tab(filebook, formats):
def restore():
""" Copy...
`{current_drive}/treebard/app/default/sample_tree_untouched.db`
as...
`{current_drive}/treebard/data/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,
user_tree_title="Sample Tree":
open_tree(
treebard,
user_tree_title))
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")
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_family_tree_id:
conn = sqlite3.connect(appwide_db_path)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
cur.execute(
"DELETE FROM family_tree WHERE family_tree_id = ?",
(self.treebard.new_family_tree_id,))
conn.commit()
self.treebard.new_family_tree_id = None
cur.close()
conn.close()
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"{current_drive}treebard/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_family_tree_id`
are referenced in `gedcom_import.py`.
"""
self.treebard.withdraw()
(self.treebard.new_family_tree, self.treebard.new_family_tree_id,
user_tree_title) = make_tree(self.treebard, default_new_tree)
self.display_tree["text"] = user_tree_title
self.tree_root_title = user_tree_title.replace(".", "").replace(
" ", "_").lower()
self.treebard.new_family_tree.withdraw()
self.treebard.deiconify()
def ok_import_gedcom(self):
self.treebard.withdraw()
elucidom_output_path = (f"{current_drive}treebard/app/python"
f"/{self.tree_root_title}_elucidom.lux")
target_unigeds_db = (f"{current_drive}treebard/data/{self.tree_root_title}"
f"/{self.tree_root_title}.tbd")
exceptions_log = (
f"{current_drive}treebard/etc/{self.tree_root_title}import_exception"
f"s_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.
"""
treebard = tk.Tk()
treebard.family_trees = {}
treebard.geometry("+75+10")
treebard.title("Treebard Genealogy Software Evidence--Assertion--Conclusion")
treebard.iconbitmap(default="{}favicon.ico".format(app_path))
treebard.protocol("WM_DELETE_WINDOW", lambda: exit_app(treebard=treebard))
treebard.maxsize(
width=treebard.winfo_screenwidth() - 12,
height=treebard.winfo_screenheight() - 36)
treebard.new_family_tree_id = None
conn = sqlite3.connect(appwide_db_path)
conn.execute('PRAGMA foreign_keys = 1')
cur = conn.cursor()
cur.execute(select_all_family_tree_ids)
all_trees = [i[0] for i in cur.fetchall()]
for tree_id in all_trees:
treebard.family_trees[tree_id] = None
# Set all trees to closed in case the app had been closed by closing the
# terminal, power outage, etc. Also `treebard.protocol("WM_DELETE_WINDOW"...`
# above apparently doesn't work (I guess the app closes before it can?)
# so below query is run every time the app opens.
cur.execute(update_family_tree_all_trees_closed)
conn.commit()
# treebard.backups_folder = get_backups_folder(cur=cur, conn=conn)
# if treebard.backups_folder is False:
# warn_too_late(treebard)
cur.close()
conn.close()
treebard.formats = make_formats_dict(root=True)
treebard.big_button = make_widgets(treebard.formats)
# Do this every toplevel window*********
configall(treebard, treebard.formats)
# **************************************
treebard.focus_force()
treebard.update_idletasks()
treebard.big_button.focus_set()
treebard.mainloop()