main.py
Nov 25, 2022 3:51:09 GMT -8
Post by Uncle Buddy on Nov 25, 2022 3:51:09 GMT -8
<drive>:\treebard_gps\app\python\main.py Last Changed 2024-02-26
# main.py
import tkinter as tk
from tkinter import filedialog
import sqlite3
from PIL import Image, ImageTk
from files import get_current_tree, current_drive, appwide_db_path, get_image_dir, app_path
from redraw import (
redraw_person_tab, get_current_person_gender, redraw_place_tab,
redraw_source_tab, redraw_current_person_area, redraw_names_tab)
from utilities import fix_tab_traversal
from widgets import (
configall, make_formats_dict, run_statusbar_tooltips, TabBook, ButtonBigPic,
open_message, RightClickMenu, make_rc_menus, EntryAutoSource, EntryAuto,
Combobox)
from do_list import DoList
from dates import DatePreferences, OK_MONTHS
from media import MediaTab
from families import NuclearFamiliesTable
from events_table import EventsTable
from places import ValidatePlace, PlacesTab, EntryAutoPlace
from gallery import Gallery
from colorizer import Colorizer, FontPicker
from messages import main_msg
from messages_context_help import main_help_msg
from search import PersonSearch
from persons import (
get_original, open_new_person_dialog, EntryAutoPerson, get_name_from_id,
NamesTab)
from graphics import CropTool, BorderTextTool, ResizeTool, CaptionTool
from dates import ChangeDate
from query_strings import (
select_media_links_main_image_name_person, update_person_gender,
select_current_person_nested_place_source, select_all_event_types,
select_person_gender_by_id, select_image_setting_default_images,
insert_event_type, select_media_links_main_image_name_nested_place,
select_media_links_main_image_name_source, update_current_image_directory,
select_preferences, select_current_image_directory,
delete_notes_links_nesting, delete_nested_place, select_current_nested_place_id,
update_event_nested_place_unknown, select_nested_place_id_not_this,
select_current_source_id, select_all_source_types, delete_change_date_source,
delete_citation_by_source, select_citation_by_source,
delete_sources_links_by_citation, delete_assertion_by_citation,
delete_sources_links, select_notes_links_assertions_by_source,
insert_citation_new, select_source_type_id, select_source_id_by_string,
update_current_source,
)
import dev_tools as dt
from dev_tools import look, seeline
# Other TabBook lists are in assertions.py and the root module.
MAIN_TABS = (
("person", "P"), ("names", "N"), ("places", "L"),
("sources", "S"), ("graphics", "G"), ("links", "K"), ("search", "H"),
("types", "T"), ("contacts", "Q"), ("preferences", "E"))
RIGHT_PANEL_TABS = (("images", "I"), ("do list", "O"))
PREFS_TABS = (
("general", "X"), ("colors", "C"), ("fonts", "F"), ("dates", "D"),
("media", "M"), ("where?", "W"))
NUKEFAM_HEADS = ("NAME OF CHILD", "GENDER", "DATE OF BIRTH", "DATE OF DEATH")
ABOUT_TREEBARD = ("Treebard GPS is free, portable, open-source, public domain "
"software written in Python, Tkinter and SQLite. Treebard's "
"purpose is to showcase functionalities that could inspire "
"developers and users of genealogy database software to expect a "
"better user experience. GPS stands for 'Genieware Pattern Simulation' "
"because GPS is here to show the way.")
ABOUT_THE_AUTHOR = ("Created 2015-2024 by Scott Robertson.\nEmail: "
"stumpednomore-at-gmail.com.\nforum/blog/code repository: "
"https://treebard.proboards.com.\nwebsite: https://treebard.com.")
def get_current_elements(family_tree_id):
conn = sqlite3.connect(appwide_db_path)
cur = conn.cursor()
tree = get_current_tree(family_tree_id, cur)[0]
cur.execute("ATTACH ? as tree", (tree,))
cur.execute(select_current_person_nested_place_source)
current_elements = cur.fetchone()
cur.execute("DETACH tree")
cur.close()
conn.close()
return current_elements
class Main(tk.Frame):
def __init__(self, master, formats, treebard, family_tree,
family_tree_id, user_tree_title, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.canvas = master
self.formats = formats
self.treebard = treebard
self.family_tree = family_tree
self.family_tree_id = family_tree_id
self.user_tree_title = user_tree_title
conn = sqlite3.connect(appwide_db_path)
cur = conn.cursor()
self.current_tree, self.current_dir = self.current_file = get_current_tree(
self.family_tree_id, cur)
cur.execute("ATTACH ? AS tree", (self.current_tree,))
(self.current_person_id, self.current_nested_place,
self.current_source) = get_current_elements(self.family_tree_id)
self.current_person_name = ""
self.current_gender = get_current_person_gender(
self.current_person_id, cur)
self.edit_image = None
self.to_destroy = []
self.male_image = ""
self.female_image = ""
self.unisex_image = ""
self.nested_place_image = ""
self.source_image = ""
self.current_image = None
self.place_data = self.family_tree.get_place_values(current_tree=self.current_tree)
self.family_tree.create_source_lists(cur=cur)
self.tabbook_x = 300
self.tabbook_y = 300
self.SCREEN_SIZE = []
self.SCREEN_SIZE.append(self.winfo_screenwidth())
self.SCREEN_SIZE.append(self.winfo_screenheight())
self.rc_menu = RightClickMenu(self.family_tree, self.treebard)
self.rc_menu.style = "rightclickmenu"
self.use_default_images = self.get_image_preferences(cur)
self.wraplength = self.formats["output_font"][1] * 21
self.make_widgets()
self.make_inputs(cur)
self.grid_widgets()
self.get_current_values()
self.nukefam_table.make_nukefam_inputs()
self.make_help_tools(family_tree)
cur.execute("DETACH tree")
cur.close()
conn.close()
def get_image_preferences(self, cur):
cur.execute(select_preferences)
use_default_images = cur.fetchone()[0]
if use_default_images == 0:
use_default_images = False
elif use_default_images == 1:
use_default_images = True
return use_default_images
def make_widgets(self):
self.current_person_area = tk.Frame(self)
self.current_person_area.style = "frame"
self.main_tabs = TabBook(
self, self.formats, dialog=self.family_tree, tabs=MAIN_TABS,
miny=0.5, minx=0.33, takefocus=0)
self.main_tabs.style = "tabbook"
self.persons_tab = tk.Frame(self.main_tabs.store["person"])
self.places_tab = tk.Frame(
self.main_tabs.store["places"], name="placetab")
self.sources_tab = tk.Frame(
self.main_tabs.store["sources"], name="sourcetab")
self.names_tab = tk.Frame(self.main_tabs.store["names"])
self.graphics_tab = tk.Frame(self.main_tabs.store["graphics"])
self.prefs_tab = tk.Frame(self.main_tabs.store["preferences"])
self.types_tab = tk.Frame(self.main_tabs.store["types"])
for widg in (
self.places_tab, self.sources_tab, self.names_tab,
self.graphics_tab, self.prefs_tab, self.types_tab):
widg.style = "frame"
self.persons_tab.style = "frame"
def make_inputs(self, cur):
self.make_current_person_area()
self.make_right_panel(cur)
self.make_person_tables()
self.make_places_tab(cur)
self.make_sources_tab(cur)
self.make_names_tab()
self.make_graphics_tab()
self.make_preferences_tab()
self.make_types_tab(cur)
self.make_general_tab()
self.make_colors_tab()
self.make_fonts_tab()
self.make_media_tab()
self.make_where_tab()
def make_current_person_area(self):
self.current_person_label = tk.Label(self.current_person_area)
self.current_person_label.style = "labelh2"
self.change_person_label = tk.Label(
self.current_person_area,
text="Change current person to:")
self.change_person_label.style = "labelh3"
self.current_person_input = EntryAutoPerson(
self.current_person_area, self.family_tree_id, self.family_tree,
autofill=True)#width=36,
self.current_person_input.style = "entry"
self.family_tree.person_autofills.append(self.current_person_input)
self.person_changer = tk.Button(
self.current_person_area, text="OK", width=6,
command=self.change_person)
self.person_search = tk.Button(
self.current_person_area,
text="WHO?", width=6,
command=self.open_person_search)
for widg in (self.person_changer, self.person_search):
widg.style = "button"
def make_right_panel(self, cur):
self.get_default_images(cur)
minx = self.tabbook_x/self.SCREEN_SIZE[0]
miny = self.tabbook_y/self.SCREEN_SIZE[1]
self.top_area = tk.Frame(self.persons_tab)
self.top_area.style = "frame"
self.right_panel = TabBook(
self.top_area, self.formats, dialog=self.family_tree,
tabs=RIGHT_PANEL_TABS, side="se", name="rightpanel",
miny=0.25, minx=0.20, takefocus=0)
self.top_pic_button = ButtonBigPic(
self.right_panel.store['images'],
command=self.open_person_gallery,
text="Add images to the current person\nin Preferences > Media tab.")
self.top_pic_button.style = "buttonbigpic"
self.do_list = DoList(
self.right_panel.store["do list"], self.formats, self.family_tree_id,
self.current_tree, self.right_panel)
for widg in (self.right_panel, self.do_list):
widg.style = "frame"
# this does not run on redraw_person_tab, just on load
if len(self.current_file[1]) != 0:
self.show_top_pic(cur)
self.update_idletasks()
def make_person_tables(self):
self.events_table = EventsTable(
self.persons_tab,
self.formats,
self.family_tree,
self.treebard,
self,
self.family_tree_id,
self.current_person_id,
self.place_data,
bg="purple"
)
self.nukefam_table = NuclearFamiliesTable(
self.top_area,
self.formats,
self.family_tree,
self.family_tree_id,
self.treebard,
self.current_person_id,
self.events_table,
self.right_panel,
current_file=self.current_file,
main=self,
bg="red"
)
for widg in (self.events_table, self.nukefam_table):
widg.style = "frame"
self.septor = tk.Frame(self.top_area, height=2)
self.septor.style = "separator"
def make_places_tab(self, cur):
self.place_frame = tk.Frame(self.places_tab)
self.place_frame.style = "frame"
self.top_pic_place_button = ButtonBigPic(
self.place_frame,
command=self.open_place_gallery,
text="Add images to the current nested place\nin Preferences > Media tab.")
self.top_pic_place_button.style = "buttonbigpic"
if len(self.current_file[1]) != 0:
self.show_top_pic_nested_place(cur)
self.current_place_display = tk.Label(
self.place_frame, justify="left")
self.current_place_display.style = "labelh3"
self.current_place_details = tk.Frame(self.place_frame) # not being used?
self.current_place_details.style = "frame"
self.placechangefrm = tk.Frame(self.places_tab)
self.placechangefrm.style = "frame"
self.placechangelab = tk.Label(
self.placechangefrm, text="Change current nested place to:")
self.placechangelab.style = "label"
self.new_current_place_input = EntryAutoPlace(
self.placechangefrm, self.family_tree,
values=self.family_tree.place_autofill_values,
autofill=True, width=60)
self.new_current_place_input.style = "entry"
self.buttons = tk.Frame(self.placechangefrm)
self.buttons.style = "frame"
self.current_place_changer = tk.Button(
self.buttons,
text="CHANGE CURRENT NESTED PLACE", width=31,
command=self.change_current_place)
self.current_place_deleter = tk.Button(
self.buttons,
text="DELETE CURRENT NESTED PLACE", width=31,
command=self.delete_current_place)
for widg in (self.current_place_changer, self.current_place_deleter):
widg.style = "button"
self.places_edit_frame = PlacesTab(
self.places_tab, self.family_tree, self.family_tree_id,
self.treebard, self, self.formats)
self.places_edit_frame.style = "frame"
cur.execute(select_current_nested_place_id)
current_nested_place_id = cur.fetchone()[0]
redraw_place_tab(
current_nested_place_id, self.family_tree, self, self.current_file,
cur, self.formats)
def make_sources_tab(self, cur):
lendir = len(self.current_dir)
self.source_frame = tk.Frame(self.sources_tab)
self.source_frame.style = "frame"
self.top_pic_source_button = ButtonBigPic(
self.source_frame,
command=self.open_source_gallery,
text="Add images to the current source\nin Preferences > Media tab.")
self.top_pic_source_button.style = "buttonbigpic"
if lendir != 0:
self.show_top_pic_source(cur)
self.current_source_display = tk.Label(self.source_frame)
self.current_source_display.style = "labelh3"
self.sourcechangelab = tk.Label(
self.sources_tab, text="Change Current Source to:", anchor="e")
self.sourcechangelab.style = "label"
self.new_current_source_input = EntryAutoSource(
self.sources_tab, self.family_tree, autofill=True, width=60,
values=self.family_tree.source_autofill_values)
self.new_current_source_input.style = "entry"
self.buttons1 = tk.Frame(self.sources_tab)
self.buttons1.style = "frame"
self.current_source_changer = tk.Button(
self.buttons1,
text="CHANGE CURRENT SOURCE",
command=self.change_current_source)
self.current_source_deleter = tk.Button(
self.buttons1,
text="DELETE CURRENT SOURCE AND CHANGE",
command=self.delete_current_source)
for widg in self.current_source_changer, self.current_source_deleter:
widg.style = "button"
self.l1 = tk.Label(
self.sources_tab, text="New source:", anchor="e")
self.l1.style = "label"
self.source_input = tk.Entry(self.sources_tab)
self.source_input.style = "entry"
self.l2 = tk.Label(
self.sources_tab, text="Source type:", anchor="e")
self.l2.style = "label"
cur.execute(select_all_source_types)
self.source_types = cur.fetchall()
self.source_type_input = Combobox(
self.sources_tab, self.formats, family_tree=self.family_tree,
values=[i[1] for i in self.source_types], entry_width=25)
self.source_type_input.style = "combobox"
self.l3 = tk.Label(
self.sources_tab, text="Citation within the source:", anchor="e")
self.l3.style = "label"
self.citation_input = tk.Entry(self.sources_tab)
self.citation_input.style = "entry"
self.source_maker = tk.Button(
self.sources_tab, text="CREATE NEW SOURCE",
command=self.make_source)
self.source_maker.style = "button"
if lendir != 0:
self.show_top_pic_source(cur)
cur.execute(select_current_source_id)
result = cur.fetchone()
if result:
current_source_id = result[0]
redraw_source_tab(cur, current_source_id, self)
def grid_sources_tab(self):
# children of sources tab
self.source_frame.grid(
column=0, row=0, sticky="news", padx=12, pady=12, columnspan=3)
self.sourcechangelab.grid(
column=0, row=1, sticky="ew", padx=(12,6), pady=(0,12))
self.new_current_source_input.grid(
column=1, row=1, sticky="ew", pady=(0,12))
self.buttons1.grid(
column=1, row=2, sticky="ew", pady=(0,12))
self.l1.grid(column=0, row=3, sticky="ew", padx=(12,6), pady=(6,0))
self.source_input.grid(
column=1, row=3, sticky="ew", pady=(6,0))
self.l2.grid(column=0, row=4, sticky="ew", padx=(12,6), pady=(6,0))
self.source_type_input.grid(
column=1, row=4, sticky="w", pady=(6,0))
self.l3.grid(column=0, row=5, sticky="ew", padx=(12,6), pady=(6,0))
self.citation_input.grid(
column=1, row=5, sticky="ew", pady=(6,0))
self.source_maker.grid(column=1, row=6, sticky="e", pady=12)
# children of self.source_frame
self.top_pic_source_button.grid(
column=0, row=0, sticky="w", padx=(0,24))
self.current_source_display.grid(
column=1, row=0, pady=12, sticky="w", padx=12)
# children of self.buttons1
self.buttons1.columnconfigure(0, weight=1)
self.buttons1.columnconfigure(1, weight=1)
self.current_source_changer.grid(column=0, row=0, sticky="ew")
self.current_source_deleter.grid(
column=1, row=0, sticky="ew", padx=(6,0))
def make_source(self, new_source=None):
if new_source is None:
new_source = self.source_input.get().strip()
if len(new_source) == 0:
return None
conn = sqlite3.connect(appwide_db_path)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
cur.execute("ATTACH ? as tree", (self.current_tree,))
source_type = self.source_type_input.entry.get().strip()
cur.execute(select_source_type_id, (source_type,))
source_type_id = cur.fetchone()[0]
cur.execute(insert_source_type, (new_source, source_type_id))
conn.commit()
new_source_id = cur.lastrowid
new_citation = self.citation_input.get().strip()
cur.execute(insert_citation_new, (new_citation, new_source_id,))
conn.commit()
for widg in (
self.source_input, self.source_type_input, self.citation_input):
widg.delete(0, "end")
ChangeDate(new_source_id, tag="source", conn=conn, cur=cur)
current_source_id = None
cur.execute(select_current_source_id)
result = cur.fetchone()
if result:
current_source_id = result[0]
redraw_source_tab(cur, current_source_id, self)
cur.execute("DETACH tree")
cur.close()
conn.close()
return new_source_id
def change_current_source(self):
new_current_source = self.new_current_source_input.get().strip()
if len(new_current_source) == 0:
return None
conn = sqlite3.connect(self.current_tree)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
cur.execute(select_source_id_by_string, (new_current_source,))
result = cur.fetchone()
if result:
new_source_id = result[0]
else:
new_source_id = self.make_source(new_source=new_current_source)
if new_source_id is None:
return None
cur.execute(update_current_source, (new_source_id,))
conn.commit()
ChangeDate(new_source_id, tag="source", conn=conn, cur=cur)
redraw_source_tab(cur, new_source_id, self)
cur.close()
conn.close()
return new_source_id
def delete_current_source(self):
conn = sqlite3.connect(self.current_tree)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
cur.execute(select_current_source_id)
result = cur.fetchone()
if result:
current_source_id = result[0]
else:
return
new_source_id = self.change_current_source()
if new_source_id is None:
return
cur.execute(delete_notes_links_source, (current_source_id,))
conn.commit()
cur.execute(select_citation_by_source, (current_source_id,))
linked_citations = [i[0] for i in cur.fetchall()]
for citation_id in linked_citations:
cur.execute(
select_notes_links_assertions_by_source, (citation_id,))
linked_assertions = [i[0] for i in cur.fetchall()]
for sources_links_id in linked_assertions:
cur.execute(delete_sources_links, (sources_links_id,))
conn.commit()
cur.execute(delete_sources_links_by_citation, (citation_id,))
conn.commit()
cur.execute(delete_assertion_by_citation, (citation_id,))
conn.commit()
cur.execute(delete_citation_by_source, (current_source_id,))
conn.commit()
cur.execute(delete_change_date_source, (current_source_id,))
conn.commit()
redraw_source_tab(cur, new_source_id, self)
cur.close()
conn.close()
def make_names_tab(self):
self.names_tab_content = NamesTab(
self.names_tab, self.treebard, self.formats, self.family_tree_id,
self, self.family_tree, self.current_person_id)
self.names_tab_content.grid(column=0, row=0)
def make_graphics_tab(self):
self.graphics_buttons = tk.Frame(self.graphics_tab)
self.graphics_buttons.style = "frame"
self.cropper = tk.Button(
self.graphics_buttons, text="CROP", width=18,
command=lambda tool="cropper": self.open_graphics_tool(tool))
self.texter = tk.Button(
self.graphics_buttons, text="ADD BORDER & TEXT", width=18,
command=lambda tool="texter": self.open_graphics_tool(tool))
self.resizer = tk.Button(
self.graphics_buttons, text="COPY/RESIZE", width=18,
command=lambda tool="resizer": self.open_graphics_tool(tool))
self.captioner = tk.Button(
self.graphics_buttons, text="EDIT CAPTION", width=18,
command=lambda tool="captioner": self.open_graphics_tool(tool))
self.clearbutt = tk.Button(
self.graphics_buttons, text="CLEAR THIS TAB",
command=self.clear_image_frame, width=18)
for widg in (
self.cropper, self.texter, self.resizer, self.captioner,
self.clearbutt):
widg.style = "button"
def make_preferences_tab(self):
self.options_tabs = TabBook(
self.prefs_tab, self.formats, dialog=self.family_tree,
tabs=PREFS_TABS, side="se",
miny=0.5, minx=0.66)
self.options_tabs.style = "tabbook"
def make_types_tab(self, cur):
cur.execute(select_all_event_types)
all_event_types = ", ".join([i[0] for i in cur.fetchall()])
self.event_types_display = tk.Label(
self.types_tab, text=all_event_types,
justify="left", wraplength=1200)#750
self.lab_event_type = tk.Label(self.types_tab,
text="Add event type if not in above list:")
self.new_event_type_input = tk.Entry(self.types_tab, width=32)
self.new_event_type_input.style = "entry"
self.rads_evt_type = tk.Frame(self.types_tab)
self.rads_evt_type.style = "frame"
self.l6 = tk.Label(self.rads_evt_type, text="Couple event?")
self.couple_rad = tk.IntVar(None, 0)
self.couple_rad1 = tk.Radiobutton(
self.rads_evt_type, variable=self.couple_rad,
value=1, text="yes")
self.couple_rad2 = tk.Radiobutton(
self.rads_evt_type, variable=self.couple_rad,
value=0, text="no")
self.l7 = tk.Label(self.rads_evt_type, text="Marital event?")
self.marital_rad = tk.IntVar(None, 0)
self.marital_rad1 = tk.Radiobutton(
self.rads_evt_type, variable=self.marital_rad,
value=1, text="yes")
self.marital_rad2 = tk.Radiobutton(
self.rads_evt_type, variable=self.marital_rad,
value=0, text="no")
self.l8 = tk.Label(self.rads_evt_type, text="After death event?")
for widg in (
self.event_types_display, self.lab_event_type, self.l6, self.l7,
self.l8):
widg.style = "label"
self.after_death_rad = tk.IntVar(None, 0)
self.after_death_rad1 = tk.Radiobutton(
self.rads_evt_type, variable=self.after_death_rad,
value=1, text="yes")
self.after_death_rad2 = tk.Radiobutton(
self.rads_evt_type, variable=self.after_death_rad,
value=0, text="no")
for widg in (
self.couple_rad1, self.couple_rad2, self.marital_rad1,
self.marital_rad2, self.after_death_rad1, self.after_death_rad2):
widg.style = "radiobutton"
self.save_event_type = tk.Button(
self.types_tab, text="SAVE", command=self.save_new_event_type)
self.save_event_type.style = "button"
def make_general_tab(self):
self.general = tk.Frame(self.options_tabs.store['general'])
self.general.style = "frame"
self.general.columnconfigure(0, weight=1)
self.general.rowconfigure(0, weight=1)
self.splash = tk.Label(self.general)
self.splash.style = "label"
splash_file = f"{app_path}/images/splash.gif"
pil_img = Image.open(splash_file)
width = pil_img.width
tk_img = ImageTk.PhotoImage(pil_img, master=self.splash)
self.splash.config(image=tk_img)
self.splash.image = tk_img
self.quotes = tk.Label(self.general, bd=1, relief='raised',
text="I would never stoop so low as to be fashionable, that's the easiest thing in the world to do.\n --Dolly Parton\n\nI'm my own grampa.\n --Moe Jaffe, Dwight Latham, 1947\n\nTime has existed since before time began.\n --Philomena Cunk",
justify="left", wraplength=480)#360
self.quotes.style = "labelheader"
self.about = tk.Label(
self.general,
text=f"{ABOUT_TREEBARD}\n\n{ABOUT_THE_AUTHOR}",
justify='left', wraplength=1200)#960
self.about.style = "label"
def make_colors_tab(self):
self.colorizer = Colorizer(
self.options_tabs.store['colors'],
self.formats,
self.family_tree,
self.family_tree_id,
self.treebard,
tabbook=self.right_panel)
self.colorizer.style = "frame"
def make_fonts_tab(self):
self.fontpicker = FontPicker(
self.options_tabs.store['fonts'], self.formats, self.family_tree_id,
self.treebard, self.family_tree, self, self.current_person_id)
self.date_options = DatePreferences(
self.options_tabs.store['dates'], self.formats, self.family_tree,
self.family_tree_id)
for widg in (self.fontpicker, self.date_options):
widg.style = "frame"
def make_media_tab(self):
self.media_tab = MediaTab(
self.options_tabs.store['media'], self.formats,
self.treebard, self.family_tree, self.family_tree_id, self)
self.media_tab.style = "frame"
def make_where_tab(self):
self.default_directories = tk.Frame(
self.options_tabs.store['where?'])
self.default_directories.style = "frame"
self.dirlab = tk.Label(
self.default_directories,
text="Default directory for image source:\n"
"(will open in file explorer)", bd=1, relief="raised")
self.dirlab.style = "labelheader"
self.dirent = tk.Entry(self.default_directories, width=60)
self.dirent.style = "entry"
imagedir = get_image_dir(self.family_tree_id)
self.dirent.insert(0, imagedir)
self.browse = tk.Button(
self.default_directories,
text=" ... ", command=self.change_image_dir)
self.browse.style = "button"
self.browse.focus_set()
def grid_widgets(self):
# children of self
self.rowconfigure(1, weight=1)
self.current_person_area.grid(
column=0, row=0, sticky='ew', pady=(0,12), columnspan=4)
self.main_tabs.grid(column=0, row=1, sticky='ew', padx=(0,12))
# children of main tabs
self.persons_tab.grid(column=0, row=0, sticky='news')
self.places_tab.grid(column=0, row=0, sticky='news')
self.sources_tab.grid(column=0, row=0, sticky='news')
self.names_tab.grid(column=0, row=0, sticky='news')
self.graphics_tab.grid(column=0, row=0, sticky='news')
self.prefs_tab.grid(column=0, row=0, sticky='news')
self.types_tab.grid(column=0, row=0, sticky='news')
# children of self.persons_tab
self.top_area.grid(
column=0, row=0, pady=12, sticky="ew")#sticky="news", padx=12,
self.events_table.grid(
column=0, row=1, padx=12, pady=12, sticky="w")
# children of self.top_area
self.top_area.columnconfigure(1, weight=1)
self.nukefam_table.grid(column=0, row=0)
self.right_panel.grid(column=1, row=0, sticky='e', padx=12, pady=12)
self.septor.grid(column=0, row=1, sticky="we", padx=(12,0), columnspan=2)
self.grid_places_tab()
self.grid_sources_tab()
self.grid_graphics_tab()
self.grid_types_tab()
self.grid_preferences_tabbook()
def grid_places_tab(self):
# children of self.places_tab
self.place_frame.grid(
column=0, row=0, sticky="news", padx=12, pady=12, columnspan=3)
self.placechangefrm.grid(
column=0, row=1, sticky="news", padx=12, pady=12, columnspan=2)
self.places_edit_frame.grid(
column=0, row=3, sticky="news", padx=12, pady=12)
# children of self.place_frame
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="w", columnspan=2)
self.current_place_details.grid(column=1, row=1, pady=12, padx=24)
# children of self.placechangefrm
self.placechangelab.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))
def grid_graphics_tab(self):
# children of self.graphics_tab
self.graphics_buttons.grid(column=0, row=0, padx=12, pady=12, sticky="ew")
# children of self.graphics_buttons
self.cropper.grid(column=0, row=0, padx=(0,6))
self.texter.grid(column=1, row=0, padx=(0,6))
self.resizer.grid(column=2, row=0, padx=(0,6))
self.captioner.grid(column=3, row=0, padx=(0,6))
self.clearbutt.grid(column=4, row=0, padx=(0,6))
def grid_types_tab(self):
# children of self.types_tab
self.event_types_display.grid(
column=0, row=0, columnspan=2, padx=12, pady=(12,0))
self.lab_event_type.grid(column=0, row=1, sticky="e")
self.new_event_type_input.grid(column=1, row=1, pady=12, sticky="w")
self.rads_evt_type.grid(column=0, row=2, columnspan=2)
self.save_event_type.grid(
column=2, row=3, pady=12, padx=(0,12),sticky="e")
# children of self.rads_evt_type
self.couple_rad1.grid(column=1, row=0)
self.couple_rad2.grid(column=2, row=0)
self.marital_rad1.grid(column=1, row=1)
self.marital_rad2.grid(column=2, row=1)
self.after_death_rad1.grid(column=1, row=2)
self.after_death_rad2.grid(column=2, row=2)
self.l6.grid(column=0, row=0, sticky="w")
self.l7.grid(column=0, row=1, sticky="w")
self.l8.grid(column=0, row=2, sticky="w")
def grid_preferences_tabbook(self):
# children of preferences tab
self.options_tabs.grid(column=0, row=0, sticky="news", padx=12, pady=12)
# children of preferences > general tab
self.general.grid(column=0, row=0, sticky='news', padx=18, pady=13)
# children of self.general
self.splash.grid(column=0, row=0, sticky="w")
self.quotes.grid(column=1, row=0, sticky="ew", ipadx=12, ipady=12, padx=18)
self.about.grid(
column=0, row=1, sticky='news', columnspan=2, pady=(18,0), padx=18)
# children of preferences > colors tab
self.colorizer.grid(column=0, row=0)
# children of preferences > fonts tab
self.fontpicker.grid(column=0, row=0)
# children of preferences > dates tab
self.date_options.grid(column=0, row=0)
# children of preferences > media tab
self.media_tab.columnconfigure(0, weight=1)
self.media_tab.rowconfigure(0, weight=1)
self.media_tab.grid(column=0, row=0, sticky="news", padx=12, pady=12)
# children of preferences > where tab
self.default_directories.columnconfigure(0, weight=1)
self.default_directories.rowconfigure(0, weight=1)
self.default_directories.grid(column=0, row=0, sticky='news', ipadx=6, ipady=6)
# children of self.current_person_area
self.current_person_area.columnconfigure(1, weight=1)
self.current_person_label.grid(
column=0, row=0, padx=(0,12), sticky="w", columnspan=4)
self.change_person_label.grid(column=0, row=1, sticky="w", padx=(0,12))
self.current_person_input.grid(column=1, row=1, sticky="ew", padx=(0,12))
self.person_changer.grid(column=2, row=1, sticky="e", padx=(0,12))
self.person_search.grid(column=3, row=1, sticky="e", padx=(0,12))
# children of images tab
self.right_panel.store['images'].columnconfigure(0, weight=1)
self.right_panel.store['images'].rowconfigure(0, weight=1)
self.top_pic_button.grid(column=0, row=0, sticky='news', padx=3, pady=12)
# children of do list tab
self.right_panel.store['do list'].columnconfigure(0, weight=1)
self.right_panel.store['do list'].rowconfigure(0, weight=1)
# self.do_list.grid(column=0, row=0, sticky='news', padx=12, pady=12)
self.do_list.grid(column=0, row=0, sticky="news")
# children of self.default_directories
self.dirlab.grid(column=0, row=0, sticky='news', padx=(12,0), pady=12)
self.dirent.grid(column=1, row=0, sticky="w", padx=(12,0), pady=12)
self.browse.grid(column=2, row=0, sticky="w", padx=12, pady=12)
def make_help_tools(self, family_tree):
self.make_statusbar_tooltips()
self.make_right_click_menu()
def make_statusbar_tooltips(self):
visited = (
(self.current_person_input,
"New Current Person Entry",
"Name of a change-to-current person will auto-fill when you "
"start typing."),
(self.person_changer,
"New Current Person OK Button",
"Press OK to change current person as per input to the left."),
(self.person_search,
"Person Search Button",
"Any name or ID of any person in the tree can be searched, "
"or a new person can be created."),
(self.top_pic_button,
"Current Person Main Image",
"The current person's main image can be clicked to open a "
"gallery of all that person's linked images."),
(self.do_list.do_list_frame,
"",
"The current family tree's to do list."),
(self.do_list.item_input,
"To Do List Item Input",
"Input new items for the current tree's to do list."),
(self.nukefam_table.parents_table,
"",
"Add parents here or add adoptive/foster parents or guardians by "
"creating an adoption/fosterage/guardianship event."),
(self.nukefam_table.progeny_table,
"",
"Add or edit partners and children of the current person and "
"the selected partner."),
(self.nukefam_table.pa_input,
"Biological Father Input",
"Add a new or existing person as the biological father of the "
"current person."),
(self.nukefam_table.ma_input,
"Biological Mother Input",
"Add a new or existing person as the biological mother of the "
"current person."),
(self.nukefam_table.new_partner_input,
"New Partner Input",
"Add a new or existing person as a partner of the current person."),
(self.nukefam_table.new_child_input,
"New Child Input",
"Add a new or existing person as a child of the current person "
"and the selected partner."),
(self.nukefam_table.partner_linker,
"Link Partner to Event Button",
"To link the selected partner to an existing couple event, click "
"the big button to highlight eligible events, then click the "
"event, then click OK."),
(self.events_table.new_conclusion_input,
"New Conclusion Input",
"Input for new conclusions including new event types."),
(self.events_table.add_event_button,
"New Conclusion Input Button",
"Press to submit new conclusion indicated to the left."),
(self.fontpicker.output_sample,
"",
"Sample text using selected output font."),
(self.fontpicker.label_sample,
"",
"Sample text using selected output font."),
(self.fontpicker.preview_button,
"Font Preview Button",
"Apply selections to sample text only."),
(self.fontpicker.font_sizer,
"Font Size Select",
"Arrow keys or mouse selects the default text size."),
(self.fontpicker.cbo,
"Font Family Select",
"Select the font family for output text."),
(self.colorizer.current_display,
"",
"Click ID to highlight currently applied swatch."),
(self.colorizer.swatch_window,
"Color Scheme Samples",
"Click color scheme to try."),
(self.colorizer.copy_button,
"Copy Color Scheme Button",
"Press to fill inputs with selected color scheme; "
"change one or more copied color."),
(self.colorizer.apply_button,
"Apply Color Scheme Button",
"Apply selected color scheme to everything."),
(self.colorizer.add_button,
"New Color Scheme Button",
"Save new color scheme using colors filled into the "
"four inputs."),
(self.colorizer.bg1,
"Background Color 1 Input",
"Type hex color string or double-click to open color chooser."),
(self.colorizer.bg2,
"Background Color 2 Input",
"Type hex color string or double-click to open color chooser."),
(self.colorizer.bg3,
"Background Color 3 Input",
"Type hex color string or double-click to open color chooser."),
(self.colorizer.fg1,
"Font Color Input",
"Type hex color string or double-click to open color chooser."),
(self.events_table.headers[0],
"",
"Press delete key to delete this conclusion row."),
(self.events_table.headers[1],
"",
"Enter simple or compound date in free order with text for "
"month, e.g.: '28 f 1845 to 1846 mar 31'."),
(self.events_table.headers[2],
"",
"Existing places will auto-fill when you start typing, "
"starting with places used most recently."),
(self.events_table.headers[3],
"",
"Use for short notes. Press button in Notes column to input "
"longer notes."),
(self.events_table.headers[4],
"",
"Age at time of event, in any format."),
(self.events_table.headers[5],
"",
"Create, add and edit roles adjunct to this event."),
(self.events_table.headers[6],
"",
"Create, add and edit notes regarding this conclusion."),
(self.events_table.headers[7],
"",
"View and edit sources, citations and assertions linked to "
"this conclusion."),
(self.date_options.test_frm,
"",
"Use top area to test input; bottom area for display settings."),
(self.date_options.prefcombos['General'],
"General Date Format",
"Select a general type of date display."),
(self.date_options.prefcombos['Estimated'],
"Estimated Date Prefix",
"Use estimated dates for unsourced guessed dates."),
(self.date_options.prefcombos['Approximate'],
"Approximate Date Prefix",
"Use approximated dates for sourced imprecise dates."),
(self.date_options.prefcombos['Calculated'],
"Calculated Date Prefix",
"Calculated dates derived from other data such as age."),
(self.date_options.prefcombos['Before/After'],
"Before or After Date Prefix",
"When you know something happened before or after some date."),
(self.date_options.prefcombos['Epoch'],
"Epoch Date Suffix",
"'BC' and 'AD' now have more politically correct variations."),
(self.date_options.prefcombos['Julian/Gregorian'],
"Calendar Era Date Suffix",
"Mark dates 'old style' or 'new style' for events during "
"calendar transition times."),
(self.date_options.prefcombos['From...To...'],
"Format Two Dates in a Span",
"Something started at one time and lasted till another time."),
(self.date_options.prefcombos['Between...And...'],
"Format Two Dates in a Range",
"Something happened within a range between two dates."),
(self.date_options.submit,
"Submit Changes",
"The changes you selected will be saved."),
(self.date_options.revert,
"Revert to Defaults",
"Date formats will revert to defaults."),
(self.media_tab.add_input,
"Add Image",
"Select an image to add to the tree's files."),
(self.media_tab.add_caption,
"Add Caption",
"Describe/explain/annotate the image."),
(self.media_tab.ok_new_image,
"OK New Image",
"Add the image to the tree."),
(self.media_tab.image_type,
"Image Type",
"Will the image be linked to a person, a place, or a source?"),
(self.media_tab.link_input,
"Link or Unlink Image",
"Select an image from the tree's files to link or unlink to an "
"element."),
(self.media_tab.radframe,
"Select Action",
"What will be done with the selected image?"),
(self.media_tab.ok_link_pic,
"OK Link/Unlink",
"Link or unlink the image from the current person, place, or "
"source."),
(self.media_tab.no_image,
"Use Default Images?",
"Use default images for elements with no linked image?"),
(self.media_tab.user_input,
"Add/Change User-defined Default Images",
"Empty field OK for 'no user-defined default image'."),
(self.media_tab.ok_default_image,
"OK Default Image",
"Add, change or delete a user-defined default image."),
(self.media_tab.reset,
"Reset Default Images",
"Delete all user-defined default images."))
run_statusbar_tooltips(
visited,
self.family_tree.statusbar.status_label,
self.family_tree.statusbar.tooltip_label,
self.family_tree)
def make_right_click_menu(self):
rcm_widgets = (
self.current_person_input,
self.person_changer,
self.person_search,
self.top_pic_button,
self.do_list.do_list_frame,
self.do_list.item_input,
self.events_table.new_conclusion_input,
self.nukefam_table.parents_table,
self.nukefam_table.progeny_table,
self.nukefam_table.pa_input,
self.nukefam_table.ma_input,
self.nukefam_table.new_partner_input,
self.nukefam_table.new_child_input,
self.nukefam_table.partner_linker,
self.events_table.headers[0],
self.events_table.headers[1],
self.events_table.headers[2],
self.events_table.headers[3],
self.events_table.headers[4],
self.events_table.headers[5],
self.events_table.headers[6],
self.events_table.headers[7],
self.fontpicker.output_sample,
self.fontpicker.label_sample,
self.fontpicker.preview_button,
self.fontpicker.font_sizer,
self.fontpicker.cbo,
self.colorizer.header,
self.colorizer.current_display,
self.colorizer.copy_button,
self.colorizer.apply_button,
self.colorizer.add_button,
self.colorizer.bg1,
self.colorizer.fg1,
self.date_options.tester_head,
self.date_options.date_test['Date Input I'],
self.date_options.date_test['Date Input II'],
self.date_options.date_test['Date Input III'],
self.date_options.pref_head,
self.date_options.prefcombos['General'],
self.date_options.prefcombos['Estimated'],
self.date_options.prefcombos['Approximate'],
self.date_options.prefcombos['Calculated'],
self.date_options.prefcombos['Before/After'],
self.date_options.prefcombos['Epoch'],
self.date_options.prefcombos['Julian/Gregorian'],
self.date_options.prefcombos['From...To...'],
self.date_options.prefcombos['Between...And...'],
self.date_options.submit, self.date_options.revert,
self.media_tab.add_input,
self.media_tab.add_caption,
self.media_tab.image_type,
self.media_tab.link_input,
self.media_tab.radframe_children[0],
self.media_tab.radframe_children[1],
self.media_tab.radframe_children[2],
self.media_tab.radframe_children[3],
self.media_tab.no_image,
self.media_tab.user_input,
self.media_tab.reset)
make_rc_menus(rcm_widgets, self.rc_menu, main_help_msg)
def change_current_place(self):
conn = sqlite3.connect(self.current_tree)
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.family_tree.place_autofill_values:
final = f"{nested_place_string}+"
else:
final = nested_place_string
ValidatePlace(
self.treebard,
self.new_current_place_input,
None,
final,
# nested_place_string,
self.place_data,
self.formats,
self.family_tree,
self.family_tree_id,
self.current_file,
self,
place_tab=True)
redraw_place_tab(
self.current_nested_place, self.family_tree, self, self.current_file,
cur, self.formats)
cur.close()
conn.close()
def delete_current_place(self):
conn = sqlite3.connect(self.current_tree)
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()
redraw_place_tab(
new_current, self.family_tree, self, self.current_file,
cur, self.formats)
cur.close()
conn.close()
def open_graphics_tool(self, tool=None, img_id=None):
def do_it(img):
if tool == "cropper":
dlg = CropTool(
self.family_tree, self.family_tree_id, self, img)
elif tool == "texter":
dlg = BorderTextTool(
self.family_tree, self.family_tree_id, self, img,
in_path=self.edit_image)
elif tool == "resizer":
dlg = ResizeTool(
self.family_tree, self.family_tree_id, self, img,
in_path=self.edit_image)
elif tool == "captioner":
dlg = CaptionTool(
self.family_tree, self.family_tree_id, self, img,
img_id=self.current_image, in_path=self.edit_image)
dlg.style = "toplevel"
if self.edit_image:
img = Image.open(self.edit_image)
else:
conn = sqlite3.connect(self.current_tree)
cur = conn.cursor()
cur.execute(select_current_image_directory)
result = cur.fetchone()
if result:
openpic_dir = result[0]
title = "Select Image to Edit"
path = filedialog.askopenfilename(
initialdir=openpic_dir,
title=title,
defaultextension=".jpg",
filetypes=(
('', '*.jpg'),
('', '*.png'),
('', '*.gif'),
('', '*.bmp'),
('all files', '*.*')))
cur.close()
conn.close()
# Prevent Attribute Error in case user closes the file dialog with
# the X button on the title bar.
if len(path) == 0:
return
img = Image.open(path)
self.edit_image = path
do_it(img)
def clear_image_frame(self):
for widg in self.to_destroy:
widg.destroy()
self.to_destroy = []
self.edit_image = None
def get_current_values(self):
self.current_person_name = self.family_tree.person_autofill_values[
self.current_person_id][0]["name"]
self.current_person_label.config(
text="Current Person (ID): {} ({})".format(
self.current_person_name, self.current_person_id))
self.current_person_label.bind("<Enter>", self.show_gender)
self.current_person_label.bind("<Leave>", self.show_id)
self.current_person_label.bind("<Double-Button-1>", self.change_gender)
self.current_person_input.values = self.family_tree.person_autofill_values
self.current_person_input.focus_force()
def change_gender(self, evt):
def ok_bend():
gender = bendervar.get()
conn = sqlite3.connect(self.current_tree)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
cur.execute(update_person_gender, (gender, self.current_person_id))
conn.commit()
cur.close()
conn.close()
redraw_current_person_area(
self.current_person_id, self, self.current_person_name,
self.current_file)
gender_bender.destroy()
def cancel_bend():
gender_bender.destroy()
bendervar = tk.StringVar(None, "unknown")
gender_bender = tk.Toplevel(self.family_tree)
gender_bender.title("Edit Gender")
gender_bender.style = "toplevel"
frm = tk.Frame(gender_bender)
frm.style = "frame"
for idx,text in enumerate(("male", "female", "unknown")):
rad = tk.Radiobutton(
frm, text=text, variable=bendervar, value=text, anchor="w")
rad.grid(column=0, row=idx, sticky="w", padx=24)
rad.style = "radiobutton"
if idx == 0:
rad.grid_configure(pady=(12,0))
buttons = tk.Frame(frm)
buttons.style = "frame"
ok_bender = tk.Button(buttons, text="OK", width=7, command=ok_bend)
cancel_bender = tk.Button(
buttons, text="CANCEL", width=7, command=cancel_bend)
for widg in ok_bender, cancel_bender:
widg.style = "button"
frm.grid(column=0, row=0, sticky="news")
buttons.grid(column=0, row=idx+1, sticky="e", padx=12, pady=12)
ok_bender.grid(column=0, row=0, sticky="e")
cancel_bender.grid(column=1, row=0, sticky="e", padx=(6,0))
configall(gender_bender, self.formats)
def show_gender(self, evt):
if self.current_gender == "U":
return
chars = len(str(self.current_person_id)) - 1
chars1 = (chars // 2) * " "
chars2 = (chars - len(chars1)) * " "
gender = f"{chars1}{self.current_gender}{chars2}"
self.current_person_label.config(
text=f"Current Person (ID): {self.current_person_name} ({gender})")
def show_id(self, evt):
self.current_person_label.config(
text="Current Person (ID): {} ({})".format(
self.current_person_name, self.current_person_id))
def open_person_search(self):
person_search_dialog = PersonSearch(
self.family_tree,
self.family_tree_id,
self.treebard,
self,
self.current_person_input,
self.events_table)
def get_default_images(self, cur):
cur.execute(select_image_setting_default_images)
default_images = [i for i in cur.fetchone()]
for idx, img in enumerate(list(default_images[0:5])):
if len(img) == 0:
default_images[idx] = default_images[idx + 5]
(self.male_image, self.female_image, self.unisex_image,
self.nested_place_image, self.source_image) = default_images[0:5]
def change_person(self):
def err_done1(entry, msg):
entry.delete(0, 'end')
msg1[0].grab_release()
msg1[0].destroy()
entry.focus_set()
got = self.current_person_input.get()
conn = sqlite3.connect(appwide_db_path)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
cur.execute("ATTACH ? as tree", (self.current_tree,))
name_data = self.current_person_input.check_name()
if name_data is None:
redo = f"{got}+"
name_data = self.current_person_input.check_name(redo=redo)
if name_data == "add_new_person":
old_current_person = self.current_person_id
self.current_person_id = open_new_person_dialog(
self.family_tree, self.family_tree_id, self.treebard,
inwidg=self.current_person_input, got=got)
if self.current_person_id is None:
self.current_person_id = old_current_person
else:
self.family_tree.person_autofill_values = self.family_tree.update_person_autofill_values()
self.current_person_name = get_name_from_id(
self.current_person_id, self.family_tree)[0]["name"]
elif name_data is None:
# Added case 20231129 to avoid error if user inputs #2 and person #2
# doesn't exist. Now the error message "no such person" is shown to
# the user twice but at least there no Python error. Fix this sometime.
return
else:
self.current_person_name = name_data[0]["name"]
self.current_person_id = name_data[1]
self.events_table.current_person_id = self.current_person_id
redraw_person_tab(
main=self,
current_person_id=self.current_person_id, family_tree=self.family_tree,
family_tree_id=self.family_tree_id, current_file=self.current_file,
conn=conn, cur=cur)
self.names_tab.name_change_table = redraw_names_tab(
self.current_person_id, self)
TabBook.resize_scrolled_dialog_with_tabbook(
self.family_tree, self.canvas, self)
# self.switch_tabs()
cur.execute("DETACH tree")
cur.close()
conn.close()
def change_image_dir(self):
imagedir = filedialog.askdirectory()
self.dirent.delete(0, "end")
self.dirent.insert(0, imagedir)
conn = sqlite3.connect(self.current_tree)
conn.execute('PRAGMA foreign_keys = 1')
cur = conn.cursor()
cur.execute(update_current_image_directory, (imagedir,))
conn.commit()
cur.close()
conn.close()
def open_person_gallery(self):
person_gallery = Gallery(
self.family_tree, self.family_tree_id, self, self.main_tabs,
self.main_tabs.store['graphics'], self.SCREEN_SIZE, tab=self.persons_tab,
current_person_id=self.current_person_id,
current_person_name=self.current_person_name)
def open_place_gallery(self):
place_gallery = Gallery(
self.family_tree, self.family_tree_id, self, self.main_tabs,
self.main_tabs.store['graphics'], self.SCREEN_SIZE, tab=self.places_tab,
current_nested_place=self.current_nested_place)
def open_source_gallery(self):
source_gallery = Gallery(
self.family_tree, self.family_tree_id, self, self.main_tabs,
self.main_tabs.store['graphics'], self.SCREEN_SIZE, tab=self.sources_tab,
current_source=self.current_source)
def resize_top_pic(self, top, new_stg):
new_width = width = top.width
new_height = height = top.height
aspect_ratio = width / height
if height != 294:
new_height = 294
new_width = int(294 * aspect_ratio)
top = top.resize((new_width, new_height))
app_path, bare = new_stg.split("images/")
bare = bare.rsplit(".", 1)[0]
thumb_path = '{}images/thumbnails/{}_resized_top_pic.png'.format(app_path, bare)
# overwrites file by same name if it exists
top.save(thumb_path)
return top
def show_top_pic(self, cur):
cur.execute(
select_media_links_main_image_name_person, (self.current_person_id,))
top_pic = cur.fetchone()
if top_pic:
img_stg = ''.join(top_pic)
new_stg = f"{current_drive}treebard_gps/data/{self.current_dir}/images/{img_stg}"
self.right_panel.active = self.right_panel.tabdict["images"][1]
self.right_panel.make_active()
elif self.use_default_images:
cur.execute(select_person_gender_by_id, (self.current_person_id,))
gender = cur.fetchone()[0]
if gender == "male":
img_stg = self.male_image
elif gender == "female":
img_stg = self.female_image
elif gender == "unknown":
img_stg = self.unisex_image
new_stg = f"{current_drive}treebard_gps/data/settings/images/{img_stg}"
elif self.use_default_images is False:
self.right_panel.active = self.right_panel.tabdict["do list"][1]
self.right_panel.make_active()
self.top_pic_button.config(image="")
self.top_pic_button.image = ""
return
top = Image.open(new_stg)
top = self.resize_top_pic(top, new_stg)
img1 = ImageTk.PhotoImage(top, master=self.canvas)
self.top_pic_button.config(image=img1)
self.top_pic_button.image = img1
self.tabbook_x = top.width
self.tabbook_y = top.height
self.update_idletasks()
def show_top_pic_nested_place(self, cur):
cur.execute(
select_media_links_main_image_name_nested_place,
(self.current_nested_place,))
top_pic_place = cur.fetchone()
if top_pic_place:
img_stg = ''.join(top_pic_place)
new_stg = f"{current_drive}treebard_gps/data/{self.current_dir}/images/{img_stg}"
elif self.use_default_images:
img_stg = self.nested_place_image
new_stg = '{}treebard_gps/data/settings/images/{}'.format(
current_drive, 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.resize_top_pic(top, new_stg)
img1 = ImageTk.PhotoImage(top, master=self.canvas)
self.top_pic_place_button.config(image=img1)
self.top_pic_place_button.image = img1
def show_top_pic_source(self, cur):
cur.execute(select_media_links_main_image_name_source, (self.current_source,))
top_pic_source = cur.fetchone()
if top_pic_source:
img_stg = ''.join(top_pic_source)
new_stg = f"{current_drive}treebard_gps/data/{self.current_dir}/images/{img_stg}"
elif self.use_default_images:
img_stg = self.source_image
new_stg = f"{current_drive}treebard_gps/data/settings/images/{img_stg}"
elif self.use_default_images is False:
self.top_pic_source_button.config(image="")
self.top_pic_source_button.image = ""
return
top = Image.open(new_stg)
top = self.resize_top_pic(top, new_stg)
img1 = ImageTk.PhotoImage(top, master=self.canvas)
self.top_pic_source_button.config(image=img1)
self.top_pic_source_button.image = img1
def save_new_event_type(self):
couple = self.couple_rad.get()
marital = self.marital_rad.get()
after_death = self.after_death_rad.get()
new_event_type = self.new_event_type_input.get()
conn = sqlite3.connect(appwide_db_path)
conn.execute("PRAGMA foreign_keys = 1")
cur = conn.cursor()
cur.execute(insert_event_type, (new_event_type, couple, marital, after_death))
conn.commit()
cur.close()
conn.close()