Change the color of the database header border (treeview) to the following: #ededee. The border color not the backround color. Python 3.12 code: import tkinter as tk
from tkinter import ttk, messagebox, font
import sqlite3
import sys

if sys.platform.startswith('win'):
try:
import ctypes
ctypes.windll.shcore.SetProcessDpiAwareness(1)
except Exception as e:
print("Error setting DPI awareness:", e)

def run_query(query, parameters=()):
with sqlite3.connect('media.db') as conn:
cursor = conn.cursor()
result = cursor.execute(query, parameters)
if query.strip().lower().startswith("select"):
return result.fetchall()
conn.commit()

def create_tables():
create_movies_table_sql = '''
CREATE TABLE IF NOT EXISTS movies (
id INTEGER PRIMARY KEY AUTOINCREMENT,
movie TEXT,
year TEXT,
present BOOLEAN,
watched BOOLEAN,
notes TEXT
);
'''
create_series_table_sql = '''
CREATE TABLE IF NOT EXISTS series (
id INTEGER PRIMARY KEY AUTOINCREMENT,
series TEXT,
seasons TEXT,
year TEXT,
present BOOLEAN,
seasons_present TEXT,
complete BOOLEAN,
watched BOOLEAN,
seasons_seen TEXT,
notes TEXT
);
'''
run_query(create_movies_table_sql)
run_query(create_series_table_sql)

class MediaTracker(tk.Tk):
def init(self):
super().init()

self.configure(bg='white')

if sys.platform.startswith('win'):
try:
scaling = self.winfo_fpixels('1i') / 72
self.tk.call('tk', 'scaling', scaling)
except Exception as e:
print("Error setting scaling:", e)

self.title("Movies and Series Tracker")
self.geometry("1200x600")

try:
self.custom_font = font.Font(family='Literata', size=12)
except:
self.custom_font = font.Font(family='Arial', size=12)

font_line_space = self.custom_font.metrics('linespace')
desired_padding = 4
row_height = font_line_space + desired_padding

style = ttk.Style()
style.theme_use("default")

style.layout(
'Custom.Treeview',
[('Custom.Treeview.treearea', {'sticky': 'nsew'})]
)
style.configure(
"Custom.Treeview",
font=self.custom_font,
rowheight=row_height,
borderwidth=0,
relief='flat',
highlightthickness=0,
background='white',
fieldbackground='white'
)

style.configure(
"Custom.Treeview.Heading",
font=(
self.custom_font.cget("family"),
self.custom_font.cget("size"),
"bold"
),
background='white',
relief='solid',
borderwidth=1,
anchor='center'
)
style.map(
"Custom.Treeview.Heading",
background=[('active', 'white')],
relief=[('active', 'solid')]
)

style.configure("Custom.TButton", font=self.custom_font)

self.sidebar_bg = '#F7F8FC'

self.grid_rowconfigure(1, weight=1)
self.grid_columnconfigure(2, weight=1)

self.header = tk.Frame(self, bg='white', height=50)
self.header.grid(row=0, column=1, columnspan=3, sticky='ew')
self.header.grid_propagate(False)

self.header_label = tk.Label(
self.header,
text="Movies",
bg='white',
font=self.custom_font,
anchor='w',
padx=10
)
self.header_label.pack(fill='x', pady=10)

self.sidebar = tk.Frame(self, width=150, bg=self.sidebar_bg)
self.sidebar.grid(row=0, column=0, rowspan=2, sticky='ns')
self.sidebar.grid_propagate(False)

self.spacer = tk.Frame(self, width=60, bg='white')
self.spacer.grid(row=1, column=1, sticky='ns')
self.spacer.grid_propagate(False)

self.content = tk.Frame(self, bg='white')
self.content.grid(row=1, column=2, sticky='nsew')

self.button_bar = tk.Frame(self, width=50, bg='white')
self.button_bar.grid(row=1, column=3, sticky='ns')
self.button_bar.grid_propagate(False)

self.create_sidebar_label("Movies", self.show_movies)
self.create_sidebar_label("Series", self.show_series)

self.add_button = ttk.Button(
self.button_bar,
text="+",
command=self.add_entry,
width=3,
style="Custom.TButton"
)
self.add_button.pack(pady=20)

self.delete_button = ttk.Button(
self.button_bar,
text="-",
command=self.delete_entry,
width=3,
style="Custom.TButton"
)
self.delete_button.pack(pady=10)

self.tree = None
self.scrollbar = None
self.current_table = None
self.current_row_id = None

self.boolean_columns = {
'movies': ['Present', 'Watched'],
'series': ['Present', 'Complete', 'Watched']
}

self.show_movies()

def create_sidebar_label(self, text, command):
label = tk.Label(
self.sidebar,
text=text,
bg=self.sidebar_bg,
font=self.custom_font,
cursor="hand2",
anchor='w',
padx=10,
pady=5
)
label.pack(pady=10, padx=0, fill='x')
label.bind("", lambda e: label.config(bg='#EAEDEF'))
label.bind("", lambda e: label.config(bg=self.sidebar_bg))
label.bind("", lambda e: command())

def clear_content(self):
if self.tree:
self.tree.destroy()
self.tree = None
if self.scrollbar:
self.scrollbar.destroy()
self.scrollbar = None

def setup_treeview(self, columns, headings):
self.tree = ttk.Treeview(
self.content,
columns=columns,
show='headings',
selectmode='browse',
style="Custom.Treeview"
)
for col, head in zip(columns, headings):
self.tree.heading(col, text=head, anchor='center')
if head.lower() == 'notes':
self.tree.column(col, width=300, anchor='w')
elif head in self.boolean_columns.get(self.current_table, []):
self.tree.column(col, width=100, anchor='center')
else:
self.tree.column(col, width=150, anchor='center')

self.scrollbar = ttk.Scrollbar(
self.content,
orient="vertical",
command=self.tree.yview
)
self.tree.configure(yscrollcommand=self.scrollbar.set)
self.scrollbar.pack(side='right', fill='y')
self.tree.pack(fill='both', expand=True)

self.tree.bind('<>', self.on_row_select)
self.tree.bind('', self.on_double_click)
self.tree.bind('', self.on_single_click)
self.tree.bind("", self.treeview_click_cursor)

def treeview_click_cursor(self, event):
region = self.tree.identify("region", event.x, event.y)
if region == "cell":
column = self.tree.identify_column(event.x)
try:
col_index = int(column.replace('#', '')) - 1
column_name = self.tree['columns'][col_index]
if column_name in self.boolean_columns.get(self.current_table, []):
self.tree.configure(cursor="hand2")
else:
self.tree.configure(cursor="")
except (IndexError, ValueError):
self.tree.configure(cursor="")
else:
self.tree.configure(cursor="")

def load_movie_data(self):
self.clear_content()
columns = ["Movie", "Year", "Present", "Watched", "Notes"]
headings = ["Movie", "Year", "Present", "Watched", "Notes"]
self.setup_treeview(columns, headings)
self.current_table = 'movies'
self.header_label.config(text="Movies")
for row in run_query('SELECT * FROM movies'):
record_id = row[0]
movie, year, present, watched, notes = row[1], row[2], row[3], row[4], row[5]
display_row = [
movie,
year,
self.bool_to_checkbox(present),
self.bool_to_checkbox(watched),
notes
]
self.tree.insert('', 'end', iid=str(record_id), values=display_row)

def load_series_data(self):
self.clear_content()
columns = [
"Series", "Seasons", "Year", "Present",
"Seasons Present", "Complete", "Watched",
"Seasons Seen", "Notes"
]
headings = [
"Series", "Seasons", "Year", "Present",
"Seasons Present", "Complete", "Watched",
"Seasons Seen", "Notes"
]
self.setup_treeview(columns, headings)
self.current_table = 'series'
self.header_label.config(text="Series")
for row in run_query('SELECT * FROM series'):
record_id = row[0]
(
series, seasons, year, present, seasons_present,
complete, watched, seasons_seen, notes
) = row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9]
display_row = [
series,
seasons,
year,
self.bool_to_checkbox(present),
seasons_present,
self.bool_to_checkbox(complete),
self.bool_to_checkbox(watched),
seasons_seen,
notes
]
self.tree.insert('', 'end', iid=str(record_id), values=display_row)

def show_movies(self):
self.load_movie_data()

def show_series(self):
self.load_series_data()

def add_entry(self):
if self.current_table == 'movies':
run_query(
"INSERT INTO movies (movie, year, present, watched, notes) "
"VALUES (?, ?, ?, ?, ?)",
('', '', False, False, '')
)
self.load_movie_data()
elif self.current_table == 'series':
run_query(
"""INSERT INTO series
(series, seasons, year, present, seasons_present, complete,
watched, seasons_seen, notes)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
('', '', '', False, '', False, False, '', '')
)
self.load_series_data()

def delete_entry(self):
selected_item = self.tree.selection()
if not selected_item:
messagebox.showwarning("No selection", "Please select a row to delete.")
return

row_id = selected_item[0]
if self.current_table == 'movies':
run_query("DELETE FROM movies WHERE id = ?", (row_id,))
self.load_movie_data()
elif self.current_table == 'series':
run_query("DELETE FROM series WHERE id = ?", (row_id,))
self.load_series_data()

def on_row_select(self, event):
selected_item = self.tree.selection()
if selected_item:
self.current_row_id = selected_item[0]
else:
self.current_row_id = None

def on_single_click(self, event):
region = self.tree.identify("region", event.x, event.y)
if region != "cell":
return
row_id = self.tree.identify_row(event.y)
column = self.tree.identify_column(event.x)
if not row_id or not column:
return
try:
col_index = int(column.replace('#', '')) - 1
except ValueError:
return
if col_index < 0 or col_index >= len(self.tree['columns']):
return
column_name = self.tree['columns'][col_index]
if self.current_table and column_name in self.boolean_columns.get(self.current_table, []):
current_value = self.tree.set(row_id, column_name)
new_value = not self.checkbox_to_bool(current_value)
self.tree.set(row_id, column_name, self.bool_to_checkbox(new_value))
row_values = self.tree.item(row_id)['values']
self.update_database(row_values)

def on_double_click(self, event):
item = self.tree.identify_row(event.y)
column = self.tree.identify_column(event.x)
if not item:
return
try:
col_num = int(column.replace('#', '')) - 1
except ValueError:
return
if (
(self.current_table == 'movies' and
self.tree['columns'][col_num] in self.boolean_columns['movies']) or
(self.current_table == 'series' and
self.tree['columns'][col_num] in self.boolean_columns['series'])
):
return
if col_num < 0 or col_num >= len(self.tree['columns']):
return
column_name = self.tree['columns'][col_num]
value = self.tree.set(item, column_name)
if self.current_table and column_name in self.boolean_columns.get(self.current_table, []):
return
bbox = self.tree.bbox(item, column)
if not bbox:
return
x, y, width, height = bbox
entry = tk.Entry(
self.content,
font=self.custom_font,
relief='flat',
borderwidth=0,
highlightthickness=0,
bg='white'
)
entry.place(x=x, y=y, width=width, height=height)
entry.insert(0, value)
entry.focus()

def save_edit(event_inner):
new_value = entry.get()
entry.destroy()
self.tree.set(item, column_name, new_value)
row_values = self.tree.item(item)['values']
self.update_database(row_values)

entry.bind('', save_edit)
entry.bind('', save_edit)

def update_database(self, row_values):
if self.current_table == 'movies':
present = self.checkbox_to_bool(row_values[2])
watched = self.checkbox_to_bool(row_values[3])
query = (
"UPDATE movies SET movie = ?, year = ?, present = ?, "
"watched = ?, notes = ? WHERE id = ?"
)
parameters = (
row_values[0], row_values[1], present, watched,
row_values[4], self.current_row_id
)
elif self.current_table == 'series':
present = self.checkbox_to_bool(row_values[3])
complete = self.checkbox_to_bool(row_values[5])
watched = self.checkbox_to_bool(row_values[6])
query = (
"""UPDATE series SET series = ?, seasons = ?, year = ?, present = ?,
seasons_present = ?, complete = ?, watched = ?, seasons_seen = ?,
notes = ? WHERE id = ?"""
)
parameters = (
row_values[0], row_values[1], row_values[2],
present, row_values[4],
complete, watched,
row_values[7], row_values[8], self.current_row_id
)
run_query(query, parameters)

@staticmethod
def bool_to_checkbox(value):
return '☑' if value else '☐'

@staticmethod
def checkbox_to_bool(value):
return True if value == '☑' else False

if name == "main":
create_tables()
app = MediaTracker()
app.mainloop()