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 tkinter as tk
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 tkinter import messagebox
from tkinter import Frame
from tkcalendar import Calendar
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
class TextScrollCombo(tk.Frame):
@ -42,13 +50,15 @@ class TextScrollCombo(tk.Frame):
self.grid_columnconfigure(0, weight=1)
# 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)
# 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')
self.txt['yscrollcommand'] = scrollb.set
self.configure(background=background_color)
def get(c,a,b):
return c.txt.get(a,b)
@ -58,9 +68,11 @@ class TitledEntry(tk.Frame):
super().__init__(root_window)
title_entry = tk.Entry(self,state=input_state,textvariable=tk.StringVar(value=init_text))
tk.Label(self, text=text).pack(side=tk.LEFT)
title_entry.pack(fill=tk.X)
self.title_entry = tk.Entry(self,state=input_state,textvariable=tk.StringVar(value=init_text),bg=background_color)
tk.Label(self, text=text, bg=background_color).pack(side=tk.LEFT)
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/
def md5Checksum(filePath):
@ -73,6 +85,47 @@ def md5Checksum(filePath):
m.update(data)
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):
data = {
@ -81,20 +134,21 @@ def main(image_path):
"capture_time_start": 0,
"capture_time_end": 0,
"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():
title = title_entry.get()
description = description_entry.get("1.0",'end-1c')
data = {
"version": "v0.0-dev",
"title": title,
"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
}
description_value = description_entry.get("1.0",'end-1c')
data["title"] = title.get()
data["capture_time_start"] = int(time.mktime(time.strptime(timestamp_start.get(), '%Y-%m-%d %H:%M:%S')))
data["capture_time_end"] = int(time.mktime(time.strptime(timestamp_end.get(), '%Y-%m-%d %H:%M:%S')))
data["description"] = description_value
with open("output.json", "w") as f:
json.dump(data, f, indent=4)
@ -104,13 +158,14 @@ def main(image_path):
# GUI setup
root = tk.Tk()
root.title("Metadata Writer")
root.configure(background=background_color)
# Load and display image
img = Image.open(image_path)
img.thumbnail((400, 400)) # Resize for display
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
@ -118,21 +173,23 @@ def main(image_path):
time_end=1547517370
timestamp=Frame(root)
timestamp.configure(bg=background_color)
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)))
timestamp_start = tk.Entry(timestamp,textvariable=start_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 end:").grid(row=0,column=2)
tk.Label(timestamp, text="Shot time/date start:", bg=background_color).grid(row=0,column=0)
tk.Label(timestamp, text="Shot time/date end:",bg=background_color).grid(row=0,column=2)
timestamp_start.grid(row=0,column=1)
timestamp_end.grid(row=0,column=3)
# Input fields
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.pack()
description.configure(bg=background_color)
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"])
version=TitledEntry(root,"Version",tk.DISABLED,data["version"])
# Save button
save_button = tk.Button(root, text="Save and Exit", command=save_and_exit)
timeline = event_timeline(root,data["events"])
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')
title .grid(row=0,column=1,sticky="we")
@ -151,6 +210,7 @@ def main(image_path):
sha512sum .grid(row=3,column=1,sticky="we")
version .grid(row=4,column=1,sticky="we")
save_button .grid(row=5,column=1)
timeline .grid(row=6,column=0,columnspan=2)
root.mainloop()