user_formats.py
Nov 25, 2022 2:55:48 GMT -8
Post by Uncle Buddy on Nov 25, 2022 2:55:48 GMT -8
d:\treebard\user_formats.py Last Changed 2024-07-25
# user_formats.py formerly colorizer.py
import tkinter as tk
from tkinter import colorchooser, font
from math import ceil
from re import search
import sqlite3
from base import Query, tree_path, tbard_path
from widgets import (
configall, make_formats_dict, Scrollbar, open_message, Combobox,
get_color_scheme_id, TabBook, LabelStay, LabelH3)
from redraw import Redraw
from messages import colorizer_msg
from base import COLOR_STRINGS
import dev_tools as dt
from dev_tools import look, seeline
def get_themes():
conx = sqlite3.connect(tbard_path)
curx = conx.cursor()
curx.execute(
''' SELECT color_scheme_id, theme FROM color_scheme
WHERE theme IS NOT null ORDER BY theme
''')
all_themes = curx.fetchall()
curx.close()
conx.close()
return all_themes
class Colorizer(tk.Frame):
''' The main organization of this class revolves around
`self.color_scheme_dicts`
which is a list of dictionaries, one for each color scheme, and
`self.current_swatch`
which is a nested dict representing one color scheme.
'''
def __init__(
self, master, formats, tree, treebard, main, tabbook=None, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.master = master
self.formats = formats
self.tree = tree
self.treebard = treebard
self.main = main
self.tabbook = tabbook
self.current_swatch = {
"widget": None, "id": 0, "column": 0, "row": 0, "Up": None,
"Down": None, "Left": None, "Right": None, "last": False,
"scheme": {"bg": "", "highlight_bg": "", "head_bg": "", "fg": ""}}
self.visible_rows = 3
self.top_visible_row = 0
self.DIREX = ("Up", "Right", "Down", "Left")
self.bind("<Map>", self.arrow_in_first)
self.tree.bind("<Return>", self.apply)
self.pad = 2
self.get_current_colors_type()
self.all_themes = get_themes()
self.make_schemes_dicts()
self.make_widgets()
self.make_swatches()
idx = self.get_applied_swatch_index(self.current_color_scheme_id)
self.applied_swatch = self.swatch_window.winfo_children()[idx]
self.make_inputs()
self.update_idletasks()
self.canvas_height = self.swatch_canvas.winfo_reqheight()
self.previewed = ('#414141', '#646464', '#878787', '#c8c8c8')
def make_widgets(self):
text = self.get_theme()
self.header = LabelH3(
self, self.formats, anchor="w",
text="Arrow keys enter & navigate swatches.")
self.current_display = LabelH3(
self, self.formats, text=text)
swatch_frame = tk.Frame(self)
self.swatch_canvas = tk.Canvas(swatch_frame)
self.sbv = Scrollbar(
swatch_frame, self.formats,
command=self.swatch_canvas.yview,
hideable=True)
self.swatch_canvas.config(yscrollcommand=self.sbv.set)
self.swatch_window = tk.Frame(self.swatch_canvas)
self.swatch_canvas.create_window(
0, 0, anchor="nw", window=self.swatch_window)
self.inputs_frame = tk.Frame(self)
self.bottom_frame = tk.Frame(self)
self.current_display.bind("<Button-1>", self.highlight_current_scheme)
# only key, button, motion, enter, leave, or virtual events
# can be bound here:
self.sbv.tag_bind("slider", "<ButtonRelease-1>", self.get)
# children of self
self.columnconfigure(0, weight=1)
self.header.grid(column=0, row=0, padx=(12,0), pady=12, sticky="ew")
self.current_display.grid(column=1, row=0, sticky="e", padx=(0,12))
swatch_frame.grid(column=0, row=1, columnspan=2, padx=12)
self.inputs_frame.grid(
column=0, row=2, columnspan=2, padx=12, pady=(12,0), sticky="ew")
self.bottom_frame.grid(
column=0, row=3, columnspan=2, padx=12, pady=(12,24), sticky="ew")
# children of swatch_frame
swatch_frame.columnconfigure(0, weight=1)
swatch_frame.rowconfigure(0, weight=1)
self.swatch_canvas.grid(column=0, row=0, sticky="news")
self.sbv.grid(column=1, row=0, sticky="ns")
self.preview_area = [
self, self.header, swatch_frame, self.swatch_window,
self.inputs_frame, self.sbv, self.current_display]
def make_inputs(self):
def do_nothing(evt):
""" Override the dropdown's normal behavior of switching to the
currently applied color scheme. Keep the scheme being previewed
in case of additional openings of the dropdown.
"""
pass
instrux = (
f"COPY a highlighted swatch to use it as a model for a new color "
f"scheme, or TYPE hex colors directly into the inputs, or "
f"DOUBLE-CLICK an input to open a color picker. Hex color format "
f"is #xxxxxx or #xxx; replace each x with a number or letter a-f.")
explanations = (
"main background color",
"main highlight color",
"secondary highlight color",
"fonts and other foreground colors")
instruxlab = tk.Label(
self.inputs_frame,
text=instrux,
wraplength=450,
anchor="se",
justify="left")
new_swatch_frame = tk.Frame(self.inputs_frame)
spacer1 = tk.Frame(new_swatch_frame)
self.bg1 = tk.Entry(new_swatch_frame, width=9, cursor="hand2")
self.bg2 = tk.Entry(new_swatch_frame, width=9, cursor="hand2")
self.bg3 = tk.Entry(new_swatch_frame, width=9, cursor="hand2")
self.fg1 = tk.Entry(new_swatch_frame, width=9, cursor="hand2")
spacer2 = tk.Frame(new_swatch_frame)
new_swatch_frame.columnconfigure(1, weight=1)
t = 1
for stg in explanations:
lab = tk.Label(new_swatch_frame, text=stg, anchor="w")
lab.grid(column=1, row=t, sticky="ew", ipadx=6, padx=(0,1))
self.preview_area.append(lab)
t += 1
self.preview_theme_lab = tk.Label(
self.inputs_frame, text="Select a theme:", anchor="w")
self.theme_selector = Combobox(
self.inputs_frame, self.formats, self.tree,
values=[i[1] for i in self.all_themes])
self.theme_selector.dropdown.bind("<Unmap>", do_nothing)
self.theme_previewer = tk.Button(
self.inputs_frame, text="PREVIEW SELECTED",
command=self.preview_theme)
instrux2 = "The sets of four colors are 'swatches'. Deleting a theme "
"just deletes a swatch's name. To delete the swatch being previewed, "
"press the Delete key."
instrux2lab = tk.Label(self.inputs_frame, text=instrux2)
self.theme_deleter = tk.Button(
self.inputs_frame, text="DELETE SELECTED",
command=self.delete_theme)
self.copy_button = tk.Button(
self.bottom_frame, text="COPY SWATCH",
command=self.fill_entries, width=19)
self.add_button = tk.Button(
self.bottom_frame, text="ADD SWATCH",
command=self.add_color_scheme, width=19)
spacer3 = tk.Frame(self.bottom_frame)
self.save_theme_lab = tk.Label(
self.bottom_frame, text="Save current swatch as theme:")
self.theme_entry = tk.Entry(self.bottom_frame)
self.theme_button = tk.Button(
self.bottom_frame, text="SAVE NEW THEME", command=self.make_theme)
spacer4 = tk.Frame(self.bottom_frame)
self.apply_button = tk.Button(
self.bottom_frame, text="APPLY", command=self.apply, width=6)
# children of self.inputs_frame
self.inputs_frame.columnconfigure(1, weight=1)
self.inputs_frame.rowconfigure(2, weight=1)
instruxlab.grid(column=0, row=0, sticky="n", rowspan=3)
new_swatch_frame.grid(
column=1, row=0, rowspan=3, padx=(12,0), sticky="ns")
self.preview_theme_lab.grid(column=2, row=0, sticky="ew")
self.theme_selector.grid(column=2, row=1, padx=(6,0), sticky="w")
self.theme_previewer.grid(column=2, row=2)
instrux2lab.grid(
column=0, row=3, columnspan=2, sticky="ew", pady=(6,0))
self.theme_deleter.grid(column=2, row=3)
# children of self.bottom_frame
self.bottom_frame.columnconfigure(2, weight=1)
self.bottom_frame.columnconfigure(6, weight=1)
self.copy_button.grid(column=0, row=0, sticky="w", pady=(6,0))
self.add_button.grid(
column=1, row=0, sticky="w", padx=(6,0), pady=(6,0))
spacer3.grid(column=2, row=0, sticky="ew", pady=(6,0))
self.save_theme_lab.grid(
column=3, row=0, sticky="e", padx=(6,0), pady=(6,0))
self.theme_entry.grid(column=4, row=0, padx=(6,0), pady=(6,0))
self.theme_button.grid(column=5, row=0, padx=(6,0), pady=(6,0))
spacer4.grid(column=6, row=0, sticky="ew", pady=(6,0))
self.apply_button.grid(
column=7, row=0, sticky="e", padx=(6,0), pady=(6,0))
# children of new_swatch_frame
new_swatch_frame.rowconfigure(0, weight=2)
new_swatch_frame.rowconfigure(5, weight=3)
spacer1.grid(column=0, row=0, columnspan=2, sticky="news")
self.bg1.grid(
column=0, row=1, padx=self.pad, pady=(self.pad, 0), sticky="ns")
self.bg2.grid(column=0, row=2, padx=self.pad, sticky="ns")
self.bg3.grid(column=0, row=3, padx=self.pad, sticky="ns")
self.fg1.grid(
column=0, row=4, padx=self.pad, pady=(0, self.pad), sticky="ns")
spacer2.grid(column=0, row=5, columnspan=2, sticky="news")
arrow_in_launches = (
self.bg1, self.bg2, self.bg3, self.fg1, self.copy_button,
self.add_button, self.apply_button)
for widg in arrow_in_launches:
for event in ("<KeyPress-Up>",):
widg.bind(event, self.arrow_in_last)
for event in ("<KeyPress-Down>",):
widg.bind(event, self.arrow_in_first)
for widg in arrow_in_launches[4:]:
for event in ("<KeyPress-Up>", "<KeyPress-Left>"):
widg.bind(event, self.arrow_in_last)
for event in ("<KeyPress-Down>", "<KeyPress-Right>"):
widg.bind(event, self.arrow_in_first)
self.preview_area.extend(
[self.copy_button, self.bg1, self.bg2, self.bg3, self.fg1,
new_swatch_frame, self.apply_button, self.add_button, spacer1,
spacer2, self.theme_entry, self.bottom_frame, self.theme_button,
self.preview_theme_lab, self.theme_selector, self.theme_selector,
self.theme_selector.entry, self.save_theme_lab, spacer3, spacer4,
self.theme_previewer, self.theme_deleter, instrux2lab, instruxlab])
self.INPUTS = (self.bg1, self.bg2, self.bg3, self.fg1)
for widg in self.INPUTS:
widg.bind("<KeyRelease>", self.validate_hex_colors)
widg.bind("<Double-Button-1>", self.open_color_chooser)
widg.bind("<space>", self.open_color_chooser)
def make_theme(self):
if self.current_color_scheme_id == 1:
return
theme = self.theme_entry.get().strip()
if len(theme) == 0 or theme in [i[1] for i in self.all_themes]:
return
if self.current_color_scheme_id is None:
return
conx = sqlite3.connect(tbard_path)
curx = conx.cursor()
curx.execute(
''' UPDATE color_scheme
SET theme = ?
WHERE color_scheme_id = ?
''',
(theme, self.current_color_scheme_id))
conx.commit()
self.theme_entry.delete(0, "end")
self.all_themes = get_themes()
self.theme_selector.config_values([i[1] for i in self.all_themes])
for dkt in self.color_scheme_dicts:
if dkt["id"] == self.current_color_scheme_id:
dkt["theme"] = theme
break
curx.close()
conx.close()
def preview_theme(self):
swatch = None
theme = self.theme_selector.entry.get().strip()
self.theme_selector.entry.delete(0, "end")
for dkt in self.color_scheme_dicts:
if dkt["theme"] == theme:
swatch = dkt["swatch"]
break
if swatch:
swatch.focus_set()
def delete_theme(self):
theme = self.theme_selector.entry.get().strip()
if theme.lower() == "dryad":
return
conx = sqlite3.connect(tbard_path)
curx = conx.cursor()
curx.execute(
''' UPDATE color_scheme
SET theme = ?
WHERE theme = ?
''',
(None, theme))
conx.commit()
self.all_themes = get_themes()
self.theme_selector.config_values([i[1] for i in self.all_themes])
self.theme_selector.entry.delete(0, "end")
curx.close()
conx.close()
def get_current_colors_type(self):
conx = sqlite3.connect(tbard_path)
curx = conx.cursor()
self.current_color_scheme_id = 1
curx.execute(
''' SELECT color_scheme_id
FROM family_tree
WHERE family_tree_id = ?
''',
(self.tree.tree_id,))
result = curx.fetchone()
if result:
self.current_color_scheme_id = result[0]
curx.close()
conx.close()
def make_schemes_dicts(self):
conx = sqlite3.connect(tbard_path)
curx = conx.cursor()
curx.execute(
''' SELECT color_scheme_id, color1, color2, color3, color4, theme
FROM color_scheme
ORDER BY theme DESC
''')
self.all_color_schemes = curx.fetchall()
self.color_scheme_dicts = []
keys = ("id", "bg", "highlight", "head", "fg", "theme")
for tup in self.all_color_schemes:
values = tup
dkt = dict(zip(keys, values))
self.color_scheme_dicts.append(dkt)
for idx, dkt in enumerate(list(self.color_scheme_dicts)):
if dkt["id"] == 1:
default_swatch = self.color_scheme_dicts.pop(idx)
self.color_scheme_dicts.insert(0, default_swatch)
break
curx.close()
conx.close()
def make_swatches(self):
qty = len(self.color_scheme_dicts)
last = qty - 1
stop = False
w = 0
c = 1
r = 0
for dkt in self.color_scheme_dicts:
frm = tk.Frame(self.swatch_window, takefocus=1)
frm.grid(column=c-1, row=r, padx=self.pad, pady=self.pad)
dkt["swatch"] = frm
frm.bind("<FocusIn>", self.highlight)
frm.bind("<FocusOut>", self.unhighlight)
frm.bind("<KeyPress>", self.traverse)
bg = dkt["bg"]
highlight = dkt["highlight"]
head = dkt["head"]
fg = dkt["fg"]
lab0 = LabelStay(frm, text=bg, bg=bg, fg=fg, width=10)
lab1 = LabelStay(frm, text=highlight, bg=highlight, fg=fg, width=10)
lab2 = LabelStay(frm, text=head, bg=head, fg=fg, width=10)
lab3 = LabelStay(frm, text=fg, bg=fg, fg=bg, width=10)
lab0.grid(column=0, row=0)
lab1.grid(column=0, row=1)
lab2.grid(column=0, row=2)
lab3.grid(column=0, row=3)
for lab in (lab0, lab1, lab2, lab3):
lab.bind("<Button-1>", self.select)
if stop is False:
stop = True
self.size_up(lab0, frm, qty)
if w == last:
self.swatch_last = frm
if c % self.swatches_across == 0:
c = c - self.swatches_across + 1
else:
c += 1
if (w + 1) % self.swatches_across == 0:
r += 1
w += 1
canvas_width = self.swatch_width * self.swatches_across
scroll_height = int(ceil(
(len(self.all_color_schemes) / self.swatches_across))) * self.swatch_height
self.swatch_canvas.config(
width=canvas_width,
height=self.swatch_height * self.visible_rows,
scrollregion=(0, 0, canvas_width, scroll_height))
def select(self, evt):
evt.widget.master.focus_set()
def size_up(self, sample_lab, sample_swatch, qty):
self.swatch_first = sample_swatch
self.update_idletasks()
self.swatch_width = sample_lab.winfo_reqwidth() + (self.pad * 2)
self.swatch_height = self.swatch_first.winfo_reqheight() + (self.pad * 2)
row_width = self.master.master.winfo_reqwidth()
self.swatches_across = int(row_width / self.swatch_width)
self.last_row = self.swatches_across - (qty % self.swatches_across) - 1
self.last_column = self.swatches_across - 1
def preview(self, typed_colors=None):
if typed_colors:
bg, highlight_bg, head_bg, fg = typed_colors
else:
bg = self.current_swatch["scheme"]["bg"]
highlight_bg = self.current_swatch["scheme"]["highlight_bg"]
head_bg = self.current_swatch["scheme"]["head_bg"]
fg = self.current_swatch["scheme"]["fg"]
self.theme_selector.dropdown.deiconify()
for widg in self.preview_area:
klass = widg.winfo_class()
subclass = type(widg).__name__
if klass == "Label":
if subclass == "LabelStay":
row = widg.grid_info()["row"] - 1
if row == 0:
widg.config(bg=bg, fg=fg)
elif row == 1:
widg.config(bg=highlight_bg, fg=fg)
elif row == 2:
widg.config(bg=head_bg, fg=fg)
elif row == 3:
widg.config(bg=fg, fg=bg)
text = widg.cget("text")
widg.config(text=text)
else:
widg.config(bg=bg)
elif klass == "Frame":
if subclass == "Combobox":
formats = {
"bg": bg, "fg": fg, "highlight_bg": highlight_bg,
"head_bg": head_bg, "font": ("dejavu sans", 12)}
widg.format_comboboxes(new_formats=formats)
widg.config_values()
else:
widg.config(bg=bg)
if klass == "Button":
widg.config(activebackground=highlight_bg, bg=bg, fg=fg)
elif klass == "Entry":
widg.config(insertbackground=fg, bg=highlight_bg)
elif klass == "Canvas":
widg.config(bg=highlight_bg)
if klass not in ("Frame", "Canvas", "Label"):
widg.config(fg=fg)
elif klass == "Label" and subclass != "LabelStay":
widg.config(fg=fg)
self.sbv.itemconfig(self.sbv.thumb, fill=bg, outline=head_bg)
self.theme_selector.dropdown.withdraw()
self.previewed = (bg, highlight_bg, head_bg, fg)
def highlight(self, evt):
self.apply_button["state"] = "normal" # ***
self.copy_button["state"] = "normal" # ***
widg = evt.widget
widg.config(bg="orange", bd=self.pad)
widg.grid_configure(padx=0, pady=0)
idx = 0
for child in self.swatch_window.winfo_children():
if child == widg:
iD = idx
break
idx += 1
idx = self.color_scheme_dicts[iD]["id"]
self.make_current_swatch_dict(widg, idx)
self.preview()
def unhighlight(self, evt):
widg = evt.widget
widg.config(bg=self.formats["highlight_bg"], bd=0)
widg.grid_configure(padx=self.pad, pady=self.pad)
def highlight_current_scheme(self, evt):
iD = int(evt.widget.cget("text").split("ID #")[1])
idx = self.get_applied_swatch_index(iD)
self.applied_swatch = self.swatch_window.winfo_children()[idx]
self.applied_swatch.config(bg="chartreuse", bd=self.pad)
self.applied_swatch.grid_configure(padx=0, pady=0)
def get_applied_swatch_index(self, iD):
q = 0
for dkt in self.color_scheme_dicts:
if dkt["id"] == iD:
idx = q
break
else:
pass
q += 1
return idx
def traverse(self, evt):
def autoscroll(sym, widg):
""" The window position can't be used, because the window is
scrolling. Have to use the canvas position. The canvas is
the port into the scrollregion. The window and scrollregion
can/should be the same size, and the canvas smaller, like a
peep-hole.
In spite of the fact that self.top_visible_row is only
referenced in this function, it has to be an instance variable
so the last value will persist till the next time the
function is called.
Arrow traversal currently works better than Tab traversal.
Tab can enter and leave the swatch area, which is useful, but
when using Tab traversal, the autoscroll doesn't work yet when
going from the end of the last visible row to the next
(non-visible) row.
Any of the arrow keys enter the swatch area, from other focused
widgets on the page except the new color Entries. Arrows don't
traverse out of the swatch area, just round and round. To get
out of the swatch area, use Tab or the mouse.
"""
self.update_idletasks()
column = widg.grid_info()["column"]
row = widg.grid_info()["row"]
swatch_top = widg.winfo_rooty()
swatch_bottom = swatch_top + self.swatch_height
up1_swatch_top = swatch_top - self.swatch_height
down1_swatch_bottom = swatch_bottom + self.swatch_height
down_ratio = swatch_top + widget_ratio / window_height
up_ratio = swatch_top - widget_ratio / window_height
self.last_visible_row = self.visible_rows - 1
if sym == "Right":
if widg == self.swatch_last:
self.scroll_up()
elif column == self.last_column:
if (row == self.last_visible_row and
self.last_visible_row != self.last_row):
self.scroll_down()
elif sym == "Left":
if widg == self.swatch_first:
self.scroll_down()
elif column == 0:
if (row == self.top_visible_row and
self.top_visible_row != 0):
self.scroll_up()
elif sym == "Down":
if self.current_swatch["last"] is True:
self.scroll_up()
elif down1_swatch_bottom > canvas_bottom:
self.scroll_down()
elif sym == "Up":
if row == 0:
self.scroll_down()
elif up1_swatch_top < canvas_top:
self.scroll_up()
sym = evt.keysym
widg = evt.widget
if sym not in ("Up", "Down", "Right", "Left"):
if sym == "Delete":
self.axe_color_scheme(widg)
return
else:
return
canvas_top = self.swatch_canvas.winfo_rooty()
window_height = self.swatch_window.winfo_reqheight()
canvas_bottom = canvas_top + self.canvas_height
widget_ratio = self.swatch_height / window_height
for direx in self.DIREX:
if sym == direx:
self.current_swatch[direx].focus_set()
autoscroll(sym, widg)
def scroll_up(self):
self.swatch_canvas.yview_moveto(0.0)
self.top_visible_row = 0
self.last_visible_row = self.visible_rows - 1
def scroll_down(self):
self.swatch_canvas.yview_moveto(1.0)
self.top_visible_row = self.last_row - self.visible_rows + 1
self.last_visible_row = self.last_row
def get_adjacent_widgets(self, widg, column, row):
""" When a swatch comes into focus, create a dict of its relevant data.
"""
catalog = widg.master.winfo_children()
right = widg.tk_focusNext()
left = widg.tk_focusPrev()
self.last_row = self.swatch_last.grid_info()["row"]
maxrow_lite = self.last_row - 1
maxcol_lastrow = self.swatch_last.grid_info()["column"]
rightcol = column + 1
leftcol = column - 1
uprow = row - 1
downrow = row + 1
last = False
if row == self.last_row:
downrow = 0
last = True
elif row == maxrow_lite:
if column > maxcol_lastrow:
downrow = 0
last = True
elif row == 0:
if column <= maxcol_lastrow:
uprow = self.last_row
else:
uprow = maxrow_lite
if column == self.last_column:
rightcol = 0
elif column == 0:
leftcol = self.last_column
for child in catalog:
if child == widg: continue
grid = child.grid_info()
newcol = grid["column"]
newrow = grid["row"]
if (newcol == rightcol and newrow == row + 1 and
column == self.last_column):
right = child
elif newcol == leftcol and newrow == row - 1 and column == 0:
left = child
elif newrow == uprow and newcol == column:
up = child
elif newrow == downrow and newcol == column:
down = child
if widg is self.swatch_first:
left = self.swatch_last
elif widg is self.swatch_last:
right = self.swatch_first
return up, right, down, left, last
def make_current_swatch_dict(self, widg, iD):
grid = widg.grid_info()
column = grid["column"]
row = grid["row"]
up, right, down, left, last = self.get_adjacent_widgets(widg, column, row)
self.current_swatch["id"] = iD
self.current_swatch["widget"] = widg
self.current_swatch["column"] = column
self.current_swatch["row"] = row
self.current_swatch["Up"] = up
self.current_swatch["Right"] = right
self.current_swatch["Down"] = down
self.current_swatch["Left"] = left
self.current_swatch["last"] = last
d = 0
for child in self.current_swatch["widget"].winfo_children():
if d == 0:
self.current_swatch["scheme"]["bg"] = child.cget("text")
elif d == 1:
self.current_swatch["scheme"]["highlight_bg"] = child.cget("text")
elif d == 2:
self.current_swatch["scheme"]["head_bg"] = child.cget("text")
elif d == 3:
self.current_swatch["scheme"]["fg"] = child.cget("text")
d += 1
def arrow_in_first(self, evt):
self.swatch_first.focus_set()
self.swatch_canvas.yview_moveto(0.0)
def arrow_in_last(self, evt):
self.swatch_last.focus_set()
self.swatch_canvas.yview_moveto(1.0)
def fill_entries(self):
self.apply_button["state"] = "disabled" # ***
colors = (
self.current_swatch["scheme"]["bg"],
self.current_swatch["scheme"]["highlight_bg"],
self.current_swatch["scheme"]["head_bg"],
self.current_swatch["scheme"]["fg"])
a = 0
for widg in self.INPUTS:
widg.delete(0, 'end')
widg.insert(0, colors[a])
a += 1
def get_theme(self):
theme = None
for tup in self.all_themes:
if tup[0] == self.current_color_scheme_id:
theme = tup[1]
break
if theme is None:
return f"Current swatch is ID #{self.current_color_scheme_id}"
else:
return f"Current swatch is ID #{self.current_color_scheme_id}: '{theme}'"
def apply(self, evt=None):
idx = self.get_applied_swatch_index(self.current_color_scheme_id)
self.applied_swatch = self.swatch_window.winfo_children()[idx]
self.applied_swatch.config(bg=self.formats["highlight_bg"], bd=0)
self.applied_swatch.grid_configure(padx=self.pad, pady=self.pad)
# get ID for above selected colors_type and change
# self.current_color_scheme_id to that id
self.current_color_scheme_id = self.current_swatch["id"]
conx = sqlite3.connect(tbard_path)
conx.execute('PRAGMA foreign_keys = 1')
curx = conx.cursor()
curx.execute(
''' UPDATE family_tree
SET color_scheme_id = ?
WHERE family_tree_id = ?
''',
(self.current_color_scheme_id, self.tree.tree_id))
conx.commit()
text = self.get_theme()
self.current_display.config(text=text)
self.tree.formats = self.main.formats = self.formats = make_formats_dict(
self.tree.tree_id, color_scheme_id=self.current_color_scheme_id)
configall(self.tree, formats=self.main.formats)
curx.close()
conx.close()
def validate_hex_colors(self, evt=None, chooser=False):
if evt:
sym = evt.keysym
if sym not in ("numbersign", "BackSpace", "Delete"):
if len(sym) != 1:
return
spelt = False
hexx = None
typed_colors = []
for widg in self.INPUTS:
typed_colors.append(widg.get().strip())
if evt or chooser is True: # KeyRelease or color chooser dialog
valid_colors = 0
else: # COPY button is pressed
valid_colors = 3
for stg in typed_colors:
hexx = search(r'^#(?:[0-9a-fA-F]{3}){1,2}$', stg)
if hexx is None and stg not in COLOR_STRINGS:
return
elif hexx is not None:
valid_colors += 1
elif stg.lower() in COLOR_STRINGS:
spelt = True
valid_colors += 1
if valid_colors == 4 and (len(stg) in (4, 7) or spelt is True):
try:
self.preview(typed_colors=typed_colors)
except tk.TclError:
pass # the error is real but it's being handled
def open_color_chooser(self, evt):
self.copy_button["state"] = "disabled" # ***
chosen_color = colorchooser.askcolor(parent=self.treebard)[1]
if chosen_color:
evt.widget.delete(0, 'end')
evt.widget.insert(0, chosen_color)
self.validate_hex_colors(chooser=True)
def add_color_scheme(self):
go = True
for ent in self.INPUTS:
if len(ent.get()) == 0:
go = False
break
if not go:
return
conx = sqlite3.connect(tbard_path)
conx.execute("PRAGMA foreign_keys = 1")
curx = conx.cursor()
for scheme in self.all_color_schemes:
if self.previewed[0:] == scheme[1:5]:
open_message(
self.treebard, colorizer_msg[0], "Non-Unique Color Scheme", "OK")
curx.close()
conx.close()
return
curx.execute(
''' INSERT INTO color_scheme (color1, color2, color3, color4)
VALUES (?, ?, ?, ?)
''',
self.previewed)
conx.commit()
self.redraw_swatches(autodown=True)
curx.close()
conx.close()
def redraw_swatches(self, autodown=False):
self.clear_inputs()
for child in self.swatch_window.winfo_children():
child.destroy()
self.make_schemes_dicts()
self.make_swatches()
self.update_idletasks()
self.canvas_height = self.swatch_canvas.winfo_reqheight()
if autodown is True:
self.scroll_down()
self.swatch_last.focus_set()
else:
self.scroll_up()
self.swatch_first.focus_set()
def axe_color_scheme(self, widg):
def delete_scheme():
curx.execute(
''' UPDATE family_tree
SET color_scheme_id = 1
WHERE family_tree_id = ?''',
(self.tree.tree_id,))
conx.commit()
curx.execute(
"DELETE from color_scheme WHERE color_scheme_id = ?",
(id_del,))
conx.commit()
conx = sqlite3.connect(tbard_path)
conx.execute("PRAGMA foreign_keys = 1")
curx = conx.cursor()
a = 0
for dkt in self.color_scheme_dicts:
if dkt["id"] == self.current_swatch["id"]:
id_del = dkt["id"]
break
a += 1
if id_del == 1:
# Don't let the user delete the default color scheme.
return
delete_scheme()
if id_del == self.current_color_scheme_id:
curx.execute(
''' UPDATE family_tree
SET color_scheme_id = 1
WHERE family_tree_id = ?
''',
(self.tree.tree_id,))
conx.commit()
self.current_color_scheme_id = 1
self.redraw_swatches()
curx.close()
conx.close()
def clear_inputs(self):
for widg in (self.bg1, self.bg2, self.bg3, self.fg1):
widg.delete(0, 'end')
def get(self, evt):
""" Haven't tried doing anything with this yet. Move it to Scrollbar
class instead of making it an instance attribute.
"""
trough_height = self.canvas_height
thumb_top = self.sbv.coords('slider')[1]
thumb_bottom = self.sbv.coords('slider')[3]
thumb_height = thumb_bottom - thumb_top
trough_traverse_height = trough_height - thumb_height
ratio = thumb_top / trough_traverse_height
# # SAVE FOR REFERENCE (these are default, user can add more):
# # treebard_fonts = [
# # 'arial', 'calibri', 'cambria', 'candara', 'comic sans ms', 'consolas',
# # 'courier new', 'dejavu sans', 'dejavu sans mono', 'franklin gothic medium',
# # 'georgia', 'microsoft sans serif', 'segoe ui', 'tahoma', 'times new roman',
# # 'trebuchet ms', 'verdana']
def get_treebard_fonts():
conx = sqlite3.connect(tbard_path)
curx = conx.cursor()
curx.execute("SELECT treebard_font_id FROM treebard_font")
results = [i[0] for i in curx.fetchall()]
treebard_fonts = sorted(results)
curx.close()
conx.close()
return treebard_fonts
DEFAULT_FONT = "verdana"
class FontPicker(tk.Frame):
def __init__(self, master, formats, treebard, tree, main, *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self.master = master
self.formats = formats
self.treebard = treebard
self.tree = tree
self.main = main
self.all_fonts = get_treebard_fonts()
query = Query()
font_scheme = []
for option in (
'font', 'font_size', 'default_font', 'default_font_size'):
result = query.select(option)
font_scheme.append(result)
copy = []
z = 0
for i in font_scheme[0:2]:
if i is None:
copy.append(font_scheme[z + 2])
else:
copy.append(font_scheme[z])
z += 1
self.font_scheme = copy
self.make_widgets()
def make_widgets(self):
sample = tk.Frame(self)
self.output_sample = tk.Label(
sample,
text="Sample Output Text ABCDEFGHxyz 0123456789 iIl1 o0O")
self.label_sample = tk.Label(sample, text="Sample Label Text")
self.fontSizeVar = tk.IntVar()
self.font_size = self.font_scheme[1]
self.font_sizer = tk.Scale(
self,
from_=8.0,
to=26.0,
tickinterval=6.0,
label="Text Size",
orient="horizontal",
length=200,
variable=self.fontSizeVar,
command=self.show_font_size)
self.font_sizer.set(self.font_size)
lab = tk.Label(self, text="Select Output Font")
self.cbo = Combobox(
self, self.formats, tree=self.tree,
values=self.all_fonts)
message = ("The sample widgets at the top show a preview, or press "
"APPLY to instantly change font family and/or size for the whole "
"application.",)
instrux = tk.Label(
self, text=message[0], wraplength=640, justify="left")
buttons = tk.Frame(self)
self.preview_button = tk.Button(
buttons, text="PREVIEW",
command=self.preview_font, width=8)
apply_button = tk.Button(
buttons, text="APPLY", command=self.apply_font, width=8)
# children of self
sample.grid(column=0, row=0)
buttons.grid(column=0, row=1, pady=12, sticky="e")
self.font_sizer.grid(column=0, row=2, pady=24)
lab.grid(column=0, row=3, pady=(24,6))
self.cbo.grid(column=0, row=4, pady=(6, 20))
instrux.grid(column=0, row=5, padx=12)
# children of sample
self.output_sample.grid(column=0, row=0, padx=24, pady=20)
# children of buttons
self.preview_button.grid(column=0, row=0, pady=12)
apply_button.grid(column=1, row=0, pady=12, padx=(6,0))
def apply_font(self):
self.font_scheme[1] = self.fontSizeVar.get()
if len(self.cbo.entry.get()) != 0:
self.font_scheme[0] = self.cbo.entry.get()
else:
self.font_scheme[0] = DEFAULT_FONT
conn = sqlite3.connect(self.tree.file)
conn.execute('PRAGMA foreign_keys = 1')
cur = conn.cursor()
font, font_size = self.font_scheme
conx = sqlite3.connect(tbard_path)
conx.execute('PRAGMA foreign_keys = 1')
curx = conx.cursor()
curx.execute(
''' UPDATE family_tree
SET (font, font_size) = (?, ?)
WHERE family_tree_id = ?
''',
(font, font_size, self.tree.tree_id))
conx.commit()
color_scheme_id = get_color_scheme_id(self.tree.tree_id)
self.tree.formats = self.formats = make_formats_dict(
self.tree.tree_id, color_scheme_id=color_scheme_id)
rdw = Redraw(main=self.main, tree=self.tree, formats=self.formats)
rdw.redraw_gui()
TabBook.resize_scrolled_dialog_with_tabbook(
self.tree, self.main.canvas, self.main)
cur.close()
conn.close()
curx.close()
conx.close()
def preview_font(self):
selected = self.cbo.entry.get()
if selected not in self.all_fonts:
selected = DEFAULT_FONT
for widg in (self.output_sample, self.label_sample):
widg.config(font=(selected, self.font_size))
def show_font_size(self, evt):
""" Possibly this first parameter is not event but the value indicated
by the slider position, which would be the same value as self.font_size.
"""
self.font_size = self.fontSizeVar.get()