Added preview image and action buttons to UI

This commit is contained in:
Brett Williams
2023-05-31 20:14:18 -05:00
parent 93b42f2717
commit 4cb1bff76e
3 changed files with 90 additions and 13 deletions

View File

@@ -1,9 +1,12 @@
import subprocess
import requests import requests
import tkinter as tk import tkinter as tk
import threading import threading
import time import time
import os import os
from tkinter import ttk from tkinter import ttk
from PIL import Image, ImageTk
from new_job_window import NewJobWindow from new_job_window import NewJobWindow
from server_proxy import RenderServerProxy from server_proxy import RenderServerProxy
@@ -36,11 +39,17 @@ class ZordonClient:
# Create a Treeview widget # Create a Treeview widget
self.root = tk.Tk() self.root = tk.Tk()
self.tree = ttk.Treeview(self.root, show="headings") self.root.title("Zordon Render Client")
self.server_proxy = RenderServerProxy(hostname='localhost') self.server_proxy = RenderServerProxy(hostname='localhost')
self.job_cache = [] self.job_cache = []
# Define the columns # Setup photo preview
self.photo_label = tk.Label(self.root, width=400, height=300)
self.photo_label.pack()
# Setup the Tree
self.tree = ttk.Treeview(self.root, show="headings")
self.tree.bind("<<TreeviewSelect>>", self.on_row_select)
self.tree["columns"] = ("id", "Name", "Renderer", "Priority", "Status", "Time Elapsed", "Frames") self.tree["columns"] = ("id", "Name", "Renderer", "Priority", "Status", "Time Elapsed", "Frames")
# Format the columns # Format the columns
@@ -57,10 +66,22 @@ class ZordonClient:
self.tree.heading(name, text=name) self.tree.heading(name, text=name)
# Pack the Treeview widget # Pack the Treeview widget
self.tree.pack(fill=tk.BOTH, expand=False) self.tree.pack(fill=tk.BOTH, expand=True)
new_job_button = tk.Button(self.root, text="New Job", command=self.show_new_job_window) button_frame = tk.Frame(self.root)
new_job_button.pack() button_frame.pack(pady=10)
# Create buttons
logs_button = tk.Button(button_frame, text="Logs", command=self.open_logs)
finder_button = tk.Button(button_frame, text="Reveal in Finder", command=self.reveal_in_finder)
self.stop_button = tk.Button(button_frame, text="Stop Job", command=self.stop_job)
add_job_button = tk.Button(button_frame, text="Add Job", command=self.show_new_job_window)
# Pack the buttons in the frame
self.stop_button.pack(side=tk.LEFT)
finder_button.pack(side=tk.LEFT)
logs_button.pack(side=tk.LEFT)
add_job_button.pack(side=tk.RIGHT)
# Start the Tkinter event loop # Start the Tkinter event loop
self.root.geometry("500x600+300+300") self.root.geometry("500x600+300+300")
@@ -68,8 +89,57 @@ class ZordonClient:
self.root.minsize(width=600, height=600) self.root.minsize(width=600, height=600)
make_sortable(self.tree) make_sortable(self.tree)
self.update_jobs()
selected_item = self.tree.get_children()[0]
self.tree.selection_set(selected_item)
self.start_update_thread() self.start_update_thread()
def stop_job(self):
selected_item = self.tree.selection()[0] # Get the selected item
row_data = self.tree.item(selected_item) # Get the text of the selected item
job_id = row_data['values'][0]
self.server_proxy.request_data(f'job/{job_id}/cancel?confirm=true')
def on_row_select(self, event):
selected_item = self.tree.selection()[0] # Get the selected item
row_data = self.tree.item(selected_item) # Get the text of the selected item
job_id = row_data['values'][0]
# update thumb
thumb_url = f'http://{self.server_proxy.hostname}:{self.server_proxy.port}/ui/job/{job_id}/thumbnail'
response = requests.get(thumb_url)
if response.status_code == 200:
import io
image_data = response.content
image = Image.open(io.BytesIO(image_data))
thumb_image = ImageTk.PhotoImage(image)
if thumb_image:
self.photo_label.configure(image=thumb_image)
self.photo_label.image = thumb_image
# update button status
job = next((d for d in self.job_cache if d.get('id') == job_id), None)
button_state = 'normal' if job['status'] == 'running' else 'disabled'
self.stop_button.config(state=button_state)
def reveal_in_finder(self):
selected_item = self.tree.selection()[0] # Get the selected item
row_data = self.tree.item(selected_item) # Get the text of the selected item
job_id = row_data['values'][0]
job = next((d for d in self.job_cache if d.get('id') == job_id), None)
output_dir = os.path.dirname(job['output_path'])
subprocess.run(['open', output_dir])
def open_logs(self):
selected_item = self.tree.selection()[0] # Get the selected item
row_data = self.tree.item(selected_item) # Get the text of the selected item
job_id = row_data['values'][0]
job = next((d for d in self.job_cache if d.get('id') == job_id), None)
subprocess.run(['open', job['log_path']])
def mainloop(self): def mainloop(self):
self.root.mainloop() self.root.mainloop()
@@ -93,12 +163,18 @@ class ZordonClient:
break break
if clear_table: if clear_table:
self.tree.delete(*self.tree.get_children()) self.tree.delete(*self.tree.get_children())
self.job_cache = self.server_proxy.get_jobs() job_fetch = self.server_proxy.get_jobs()
all_jobs = self.job_cache if job_fetch:
for job in all_jobs: self.job_cache = job_fetch # update the cache only if its good data
for job in self.job_cache:
display_status = job['status'] if job['status'] != 'running' else job['percent_complete'] display_status = job['status'] if job['status'] != 'running' else job['percent_complete']
values = (job['id'], job['name'] or os.path.basename(job['input_path']), job['renderer'] + "-" + job['renderer_version'], job['priority'], values = (job['id'],
display_status, job['time_elapsed'], job['total_frames']) job['name'] or os.path.basename(job['input_path']),
job['renderer'] + "-" + job['renderer_version'],
job['priority'],
display_status,
job['time_elapsed'],
job['total_frames'])
if self.tree.exists(job['id']): if self.tree.exists(job['id']):
update_row(self.tree, job['id'], new_values=values) update_row(self.tree, job['id'], new_values=values)
else: else:
@@ -108,7 +184,7 @@ class ZordonClient:
new_window = tk.Toplevel(self.root) new_window = tk.Toplevel(self.root)
new_window.title("New Window") new_window.title("New Window")
new_window.geometry("500x600+300+300") new_window.geometry("500x600+300+300")
new_window.resizable(False, False) new_window.resizable(False, height=True)
x = NewJobWindow(parent=new_window, hostname=self.server_proxy.hostname) x = NewJobWindow(parent=new_window, hostname=self.server_proxy.hostname)
x.pack() x.pack()

View File

@@ -73,7 +73,7 @@ class NewJobWindow(Frame):
self.renderer_info = {} self.renderer_info = {}
self.priority = IntVar(value=2) self.priority = IntVar(value=2)
self.master.title("Schedule Job") self.master.title("New Job")
self.pack(fill=BOTH, expand=True) self.pack(fill=BOTH, expand=True)
# project frame # project frame

View File

@@ -324,7 +324,8 @@ class BaseRenderWorker(Base):
'renderer_version': self.renderer_version, 'renderer_version': self.renderer_version,
'errors': getattr(self, 'errors', None), 'errors': getattr(self, 'errors', None),
'total_frames': self.total_frames, 'total_frames': self.total_frames,
'last_output': getattr(self, 'last_output', None) 'last_output': getattr(self, 'last_output', None),
'log_path': self.log_path()
} }
# convert to json and back to auto-convert dates to iso format # convert to json and back to auto-convert dates to iso format