Added event timeline, background color setting and fixed file writing

This commit is contained in:
(Tim) Efthimis Kritikos 2025-05-28 23:30:22 +01:00
parent 9a677bc090
commit 7edbf9f200

View File

@ -22,12 +22,20 @@ import sys
import json import json
import tkinter as tk import tkinter as tk
import hashlib import hashlib
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,NavigationToolbar2Tk)
from datetime import datetime
from time import strftime, localtime from time import strftime, localtime
from tkinter import messagebox from tkinter import messagebox
from tkinter import Frame from tkinter import Frame
from tkcalendar import Calendar from tkcalendar import Calendar
from PIL import Image, ImageTk from PIL import Image, ImageTk
background_color='#DDDDDD'
#Got TextScrollCombo from stack overflow https://stackoverflow.com/questions/13832720/how-to-attach-a-scrollbar-to-a-text-widget #Got TextScrollCombo from stack overflow https://stackoverflow.com/questions/13832720/how-to-attach-a-scrollbar-to-a-text-widget
class TextScrollCombo(tk.Frame): class TextScrollCombo(tk.Frame):
@ -42,13 +50,15 @@ class TextScrollCombo(tk.Frame):
self.grid_columnconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1)
# create a Text widget # create a Text widget
self.txt = tk.Text(self,height=10) self.txt = tk.Text(self,height=10,bg=background_color)
self.txt.grid(row=0, column=0, sticky="nsew", padx=2, pady=2) self.txt.grid(row=0, column=0, sticky="nsew", padx=2, pady=2)
# create a Scrollbar and associate it with txt # create a Scrollbar and associate it with txt
scrollb = tk.Scrollbar(self, command=self.txt.yview) scrollb = tk.Scrollbar(self, command=self.txt.yview,bg=background_color)
scrollb.grid(row=0, column=1, sticky='nsew') scrollb.grid(row=0, column=1, sticky='nsew')
self.txt['yscrollcommand'] = scrollb.set self.txt['yscrollcommand'] = scrollb.set
self.configure(background=background_color)
def get(c,a,b): def get(c,a,b):
return c.txt.get(a,b) return c.txt.get(a,b)
@ -58,9 +68,11 @@ class TitledEntry(tk.Frame):
super().__init__(root_window) super().__init__(root_window)
title_entry = tk.Entry(self,state=input_state,textvariable=tk.StringVar(value=init_text)) self.title_entry = tk.Entry(self,state=input_state,textvariable=tk.StringVar(value=init_text),bg=background_color)
tk.Label(self, text=text).pack(side=tk.LEFT) tk.Label(self, text=text, bg=background_color).pack(side=tk.LEFT)
title_entry.pack(fill=tk.X) self.title_entry.pack(fill=tk.X)
def get(c):
return c.title_entry.get()
#Got md5Checksum from someones blog https://www.joelverhagen.com/blog/2011/02/md5-hash-of-file-in-python/ #Got md5Checksum from someones blog https://www.joelverhagen.com/blog/2011/02/md5-hash-of-file-in-python/
def md5Checksum(filePath): def md5Checksum(filePath):
@ -73,6 +85,47 @@ def md5Checksum(filePath):
m.update(data) m.update(data)
return m.hexdigest() return m.hexdigest()
def event_timeline(window,events):
plot_line_width=0.8
fig, ax = plt.subplots(figsize=(12, 1.8), constrained_layout=True)
ax.set_facecolor('none') # Comment out to debug out of bound graph
fig.patch.set_facecolor('none')
ax.set_position([.01,0,0.8,1])
timelines=[]
labels=[]
for item in events:
labels.append(item["text"])
timelines.append(datetime.fromtimestamp(item["time"]))
offsets= [3,2,1]
levels = np.tile(offsets, int(np.ceil(len(timelines)/len(offsets))))[:len(timelines)]
ax.axhline(0, c="black",linewidth=2)
ax.vlines(timelines, 0, levels, color='black',linewidth=plot_line_width ) #Draw event lines
ax.plot(timelines, np.zeros_like(timelines), "-o", color="k", markerfacecolor="w",linewidth=plot_line_width ) #Draw the line with the points
for t, l, b in zip(timelines, levels, labels):
ax.annotate(b+"\n"+t.strftime("%d/%m/%Y"), xy=(t, l),
xytext=(0, -20), textcoords='offset points',
horizontalalignment='left',
verticalalignment='bottom' if l > 0 else 'top',
color='black',
fontsize=9,bbox=dict(facecolor=background_color, edgecolor='black', boxstyle='round,pad=.5', linewidth=plot_line_width)
)
ax.yaxis.set_visible(False)
ax.spines[["left", "top", "right"]].set_visible(False)
ax.spines['bottom'].set_position(('data', -8000))
canvas = FigureCanvasTkAgg(fig, master = window)
canvas.draw()
return canvas.get_tk_widget()
def main(image_path): def main(image_path):
data = { data = {
@ -81,20 +134,21 @@ def main(image_path):
"capture_time_start": 0, "capture_time_start": 0,
"capture_time_end": 0, "capture_time_end": 0,
"image_sha512": md5Checksum(image_path), "image_sha512": md5Checksum(image_path),
"description": "" "description": "",
"events" : [{ "time": 1733055790, "text": "Data captured" },
{ "time": 1741745288, "text": "Raw file developed"},
{ "time": 1747012088, "text": "Metadata written" },
{ "time": 1747876088, "text": "Metadata modified" },
{ "time": 1759876088, "text": "Metadata version updated" }
]
} }
def save_and_exit(): def save_and_exit():
title = title_entry.get() description_value = description_entry.get("1.0",'end-1c')
description = description_entry.get("1.0",'end-1c') data["title"] = title.get()
data = { data["capture_time_start"] = int(time.mktime(time.strptime(timestamp_start.get(), '%Y-%m-%d %H:%M:%S')))
"version": "v0.0-dev", data["capture_time_end"] = int(time.mktime(time.strptime(timestamp_end.get(), '%Y-%m-%d %H:%M:%S')))
"title": title, data["description"] = description_value
"capture_time_start": int(time.mktime(time.strptime(timestamp_start.get(), '%Y-%m-%d %H:%M:%S'))),
"capture_time_end": int(time.mktime(time.strptime(timestamp_end.get(), '%Y-%m-%d %H:%M:%S'))),
"image_sha512": md5Checksum(image_path),
"description": description
}
with open("output.json", "w") as f: with open("output.json", "w") as f:
json.dump(data, f, indent=4) json.dump(data, f, indent=4)
@ -104,13 +158,14 @@ def main(image_path):
# GUI setup # GUI setup
root = tk.Tk() root = tk.Tk()
root.title("Metadata Writer") root.title("Metadata Writer")
root.configure(background=background_color)
# Load and display image # Load and display image
img = Image.open(image_path) img = Image.open(image_path)
img.thumbnail((400, 400)) # Resize for display img.thumbnail((400, 400)) # Resize for display
photo = ImageTk.PhotoImage(img) photo = ImageTk.PhotoImage(img)
img_label = tk.Label(root, image=photo) img_label = tk.Label(root, image=photo, bg=background_color)
img_label.image = photo # keep a reference img_label.image = photo # keep a reference
@ -118,21 +173,23 @@ def main(image_path):
time_end=1547517370 time_end=1547517370
timestamp=Frame(root) timestamp=Frame(root)
timestamp.configure(bg=background_color)
start_var = tk.StringVar(value=strftime('%Y-%m-%d %H:%M:%S', localtime(time_start))) start_var = tk.StringVar(value=strftime('%Y-%m-%d %H:%M:%S', localtime(time_start)))
end_var = tk.StringVar(value=strftime('%Y-%m-%d %H:%M:%S', localtime(time_end))) end_var = tk.StringVar(value=strftime('%Y-%m-%d %H:%M:%S', localtime(time_end)))
timestamp_start = tk.Entry(timestamp,textvariable=start_var) timestamp_start = tk.Entry(timestamp,textvariable=start_var)
timestamp_end = tk.Entry(timestamp,textvariable=end_var) timestamp_end = tk.Entry(timestamp,textvariable=end_var)
tk.Label(timestamp, text="Shot time/date start:").grid(row=0,column=0) tk.Label(timestamp, text="Shot time/date start:", bg=background_color).grid(row=0,column=0)
tk.Label(timestamp, text="Shot time/date end:").grid(row=0,column=2) tk.Label(timestamp, text="Shot time/date end:",bg=background_color).grid(row=0,column=2)
timestamp_start.grid(row=0,column=1) timestamp_start.grid(row=0,column=1)
timestamp_end.grid(row=0,column=3) timestamp_end.grid(row=0,column=3)
# Input fields # Input fields
description=Frame(root) description=Frame(root)
tk.Label(description, text="Description:").pack(side=tk.LEFT) tk.Label(description, text="Description:",bg=background_color).pack(side=tk.LEFT)
description_entry = TextScrollCombo(description) description_entry = TextScrollCombo(description)
description_entry.pack() description_entry.pack()
description.configure(bg=background_color)
description_entry.config(width=600, height=100) description_entry.config(width=600, height=100)
@ -140,9 +197,11 @@ def main(image_path):
sha512sum=TitledEntry(root,"Image SHA512",tk.DISABLED,data["image_sha512"]) sha512sum=TitledEntry(root,"Image SHA512",tk.DISABLED,data["image_sha512"])
version=TitledEntry(root,"Version",tk.DISABLED,data["version"]) version=TitledEntry(root,"Version",tk.DISABLED,data["version"])
# Save button timeline = event_timeline(root,data["events"])
save_button = tk.Button(root, text="Save and Exit", command=save_and_exit) timeline.configure(bg=background_color)
# Save button
save_button = tk.Button(root, text="Save and Exit", command=save_and_exit, bg=background_color)
img_label .grid(row=0,column=0,rowspan=6,sticky='n') img_label .grid(row=0,column=0,rowspan=6,sticky='n')
title .grid(row=0,column=1,sticky="we") title .grid(row=0,column=1,sticky="we")
@ -151,6 +210,7 @@ def main(image_path):
sha512sum .grid(row=3,column=1,sticky="we") sha512sum .grid(row=3,column=1,sticky="we")
version .grid(row=4,column=1,sticky="we") version .grid(row=4,column=1,sticky="we")
save_button .grid(row=5,column=1) save_button .grid(row=5,column=1)
timeline .grid(row=6,column=0,columnspan=2)
root.mainloop() root.mainloop()