mirror of
https://github.com/blw1138/Zordon.git
synced 2025-12-17 08:48:13 +00:00
New client work
This commit is contained in:
@@ -30,7 +30,7 @@ status_colors = {RenderStatus.ERROR: "red", RenderStatus.CANCELLED: 'orange1', R
|
||||
RenderStatus.RUNNING: 'cyan'}
|
||||
|
||||
categories = [RenderStatus.RUNNING, RenderStatus.ERROR, RenderStatus.NOT_STARTED, RenderStatus.SCHEDULED,
|
||||
RenderStatus.COMPLETED, RenderStatus.CANCELLED]
|
||||
RenderStatus.COMPLETED, RenderStatus.CANCELLED, RenderStatus.UNDEFINED]
|
||||
|
||||
renderer_colors = {'ffmpeg': '[magenta]', 'blender': '[orange1]', 'aerender': '[purple]'}
|
||||
|
||||
|
||||
118
lib/client/client.py
Normal file
118
lib/client/client.py
Normal file
@@ -0,0 +1,118 @@
|
||||
import requests
|
||||
import tkinter as tk
|
||||
import threading
|
||||
import time
|
||||
import os
|
||||
from tkinter import ttk
|
||||
from new_job_window import NewJobWindow
|
||||
from server_proxy import RenderServerProxy
|
||||
|
||||
|
||||
def request_data(server_ip, payload, server_port=8080, timeout=2):
|
||||
try:
|
||||
req = requests.get(f'http://{server_ip}:{server_port}/api/{payload}', timeout=timeout)
|
||||
if req.ok:
|
||||
return req.json()
|
||||
except Exception as e:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def sort_column(tree, col, reverse=False):
|
||||
data = [(tree.set(child, col), child) for child in tree.get_children('')]
|
||||
data.sort(reverse=reverse)
|
||||
for index, (_, child) in enumerate(data):
|
||||
tree.move(child, '', index)
|
||||
|
||||
|
||||
def make_sortable(tree):
|
||||
for col in tree["columns"]:
|
||||
tree.heading(col, text=col, command=lambda c=col: sort_column(tree, c))
|
||||
|
||||
|
||||
class ZordonClient:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
# Create a Treeview widget
|
||||
self.root = tk.Tk()
|
||||
self.tree = ttk.Treeview(self.root, show="headings")
|
||||
self.server_proxy = RenderServerProxy(hostname='localhost')
|
||||
self.job_cache = []
|
||||
|
||||
# Define the columns
|
||||
self.tree["columns"] = ("id", "Name", "Renderer", "Priority", "Status", "Time Elapsed", "Frames")
|
||||
|
||||
# Format the columns
|
||||
self.tree.column("id", width=0, stretch=False)
|
||||
self.tree.column("Name", width=50)
|
||||
self.tree.column("Renderer", width=100, stretch=False)
|
||||
self.tree.column("Priority", width=50, stretch=False)
|
||||
self.tree.column("Status", width=100, stretch=False)
|
||||
self.tree.column("Time Elapsed", width=100, stretch=False)
|
||||
self.tree.column("Frames", width=50, stretch=False)
|
||||
|
||||
# Create the column headings
|
||||
for name in self.tree['columns']:
|
||||
self.tree.heading(name, text=name)
|
||||
|
||||
# Pack the Treeview widget
|
||||
self.tree.pack(fill=tk.BOTH, expand=False)
|
||||
|
||||
new_job_button = tk.Button(self.root, text="New Job", command=self.show_new_job_window)
|
||||
new_job_button.pack()
|
||||
|
||||
# Start the Tkinter event loop
|
||||
self.root.geometry("500x600+300+300")
|
||||
self.root.maxsize(width=2000, height=1200)
|
||||
self.root.minsize(width=600, height=600)
|
||||
|
||||
make_sortable(self.tree)
|
||||
self.start_update_thread()
|
||||
|
||||
def mainloop(self):
|
||||
self.root.mainloop()
|
||||
|
||||
def start_update_thread(self):
|
||||
x = threading.Thread(target=self.__background_update)
|
||||
x.daemon = True
|
||||
x.start()
|
||||
|
||||
def __background_update(self):
|
||||
while True:
|
||||
self.update_jobs(clear_table=False)
|
||||
time.sleep(1)
|
||||
|
||||
def update_jobs(self, clear_table=False):
|
||||
|
||||
def update_row(tree, id, new_values):
|
||||
for item in tree.get_children():
|
||||
values = tree.item(item, "values")
|
||||
if values[0] == id:
|
||||
tree.item(item, values=new_values)
|
||||
break
|
||||
if clear_table:
|
||||
self.tree.delete(*self.tree.get_children())
|
||||
self.job_cache = self.server_proxy.get_jobs()
|
||||
all_jobs = self.job_cache
|
||||
for job in all_jobs:
|
||||
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'],
|
||||
display_status, job['time_elapsed'], job['total_frames'])
|
||||
if self.tree.exists(job['id']):
|
||||
update_row(self.tree, job['id'], new_values=values)
|
||||
else:
|
||||
self.tree.insert("", tk.END, iid=job['id'], values=values)
|
||||
|
||||
def show_new_job_window(self):
|
||||
new_window = tk.Toplevel(self.root)
|
||||
new_window.title("New Window")
|
||||
new_window.geometry("500x600+300+300")
|
||||
new_window.resizable(False, False)
|
||||
x = NewJobWindow(parent=new_window, hostname=self.server_proxy.hostname)
|
||||
x.pack()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
x = ZordonClient()
|
||||
x.mainloop()
|
||||
@@ -13,7 +13,7 @@ import psutil
|
||||
import requests
|
||||
|
||||
from lib.render_workers.blender_worker import Blender
|
||||
from lib.utilities.server_helper import post_job_to_server
|
||||
from server_proxy import RenderServerProxy
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
@@ -61,12 +61,12 @@ class ChecklistBox(Frame):
|
||||
return values
|
||||
|
||||
|
||||
class ScheduleJob(Frame):
|
||||
class NewJobWindow(Frame):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
def __init__(self, parent=None, hostname=None):
|
||||
super().__init__(parent)
|
||||
|
||||
self.server_hostname = None
|
||||
self.server_proxy = RenderServerProxy(hostname=hostname)
|
||||
self.chosen_file = None
|
||||
self.clients = []
|
||||
self.presets = {}
|
||||
@@ -76,9 +76,6 @@ class ScheduleJob(Frame):
|
||||
self.master.title("Schedule Job")
|
||||
self.pack(fill=BOTH, expand=True)
|
||||
|
||||
self.server_button = Button(self, text="", width=6, command=self.request_new_hostname)
|
||||
self.server_button.pack(fill=X, padx=5, expand=False)
|
||||
|
||||
# project frame
|
||||
job_frame = LabelFrame(self, text="Job Settings")
|
||||
job_frame.pack(fill=X, padx=5, pady=5)
|
||||
@@ -156,24 +153,12 @@ class ScheduleJob(Frame):
|
||||
self.custom_args_entry = None
|
||||
self.submit_frame = None
|
||||
|
||||
if os.path.exists(prefs_name):
|
||||
with open(prefs_name, 'r') as file:
|
||||
hostname = file.read()
|
||||
server_data = request_data(hostname, 'status', timeout=server_setup_timeout)
|
||||
if server_data:
|
||||
self.set_hostname(hostname)
|
||||
if not self.server_hostname:
|
||||
server_data = request_data('localhost', 'status', timeout=server_setup_timeout)
|
||||
if server_data:
|
||||
self.set_hostname(server_data['host_name'])
|
||||
else:
|
||||
self.request_new_hostname()
|
||||
self.fetch_server_data()
|
||||
|
||||
def fetch_server_data(self):
|
||||
self.clients = request_data(self.server_hostname, 'clients', timeout=3) or []
|
||||
self.renderer_info = request_data(self.server_hostname, 'renderer_info', timeout=3) or {}
|
||||
self.presets = request_data(self.server_hostname, 'presets', timeout=3) or {}
|
||||
self.clients = self.server_proxy.request_data('clients', timeout=3) or []
|
||||
self.renderer_info = self.server_proxy.request_data('renderer_info', timeout=3) or {}
|
||||
self.presets = self.server_proxy.request_data('presets', timeout=3) or {}
|
||||
|
||||
# update clients
|
||||
self.client_combo['values'] = self.clients
|
||||
@@ -388,7 +373,7 @@ class ScheduleJob(Frame):
|
||||
|
||||
# Submit to server
|
||||
job_list = job_list or [job_json]
|
||||
result = post_job_to_server(input_path=input_path, job_list=job_list, hostname=client)
|
||||
result = self.server_proxy.post_job_to_server(input_path=input_path, job_list=job_list)
|
||||
if result.ok:
|
||||
messagebox.showinfo("Success", "Job successfully submitted to server.")
|
||||
else:
|
||||
@@ -405,7 +390,7 @@ def main():
|
||||
root.geometry("500x600+300+300")
|
||||
root.maxsize(width=1000, height=2000)
|
||||
root.minsize(width=600, height=600)
|
||||
app = ScheduleJob()
|
||||
app = NewJobWindow(root)
|
||||
root.mainloop()
|
||||
|
||||
|
||||
54
lib/client/server_proxy.py
Normal file
54
lib/client/server_proxy.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import os
|
||||
import json
|
||||
import requests
|
||||
from lib.render_workers.base_worker import RenderStatus
|
||||
|
||||
status_colors = {RenderStatus.ERROR: "red", RenderStatus.CANCELLED: 'orange1', RenderStatus.COMPLETED: 'green',
|
||||
RenderStatus.NOT_STARTED: "yellow", RenderStatus.SCHEDULED: 'purple',
|
||||
RenderStatus.RUNNING: 'cyan'}
|
||||
|
||||
categories = [RenderStatus.RUNNING, RenderStatus.ERROR, RenderStatus.NOT_STARTED, RenderStatus.SCHEDULED,
|
||||
RenderStatus.COMPLETED, RenderStatus.CANCELLED, RenderStatus.UNDEFINED]
|
||||
|
||||
|
||||
class RenderServerProxy:
|
||||
|
||||
def __init__(self, hostname=None, server_port="8080"):
|
||||
self.hostname = hostname
|
||||
self.port = server_port
|
||||
self.fetched_status_data = None
|
||||
|
||||
def connect(self):
|
||||
status = self.request_data('status')
|
||||
return status
|
||||
|
||||
def request_data(self, payload, timeout=5):
|
||||
try:
|
||||
req = requests.get(f'http://{self.hostname}:{self.port}/api/{payload}', timeout=timeout)
|
||||
if req.ok:
|
||||
return req.json()
|
||||
except Exception as e:
|
||||
pass
|
||||
return None
|
||||
|
||||
def get_jobs(self):
|
||||
all_jobs = self.request_data('jobs')
|
||||
sorted_jobs = []
|
||||
if all_jobs:
|
||||
for status_category in categories:
|
||||
found_jobs = [x for x in all_jobs if x['status'] == status_category.value]
|
||||
if found_jobs:
|
||||
sorted_jobs.extend(found_jobs)
|
||||
return sorted_jobs
|
||||
|
||||
def get_data(self, timeout=5):
|
||||
all_data = self.request_data('full_status', timeout=timeout)
|
||||
return all_data
|
||||
|
||||
def post_job_to_server(self, input_path, job_list):
|
||||
# Pack job data and submit to server
|
||||
job_files = {'file': (os.path.basename(input_path), open(input_path, 'rb'), 'application/octet-stream'),
|
||||
'json': (None, json.dumps(job_list), 'application/json')}
|
||||
|
||||
req = requests.post(f'http://{self.hostname}:{self.port}/api/add_job', files=job_files)
|
||||
return req
|
||||
Reference in New Issue
Block a user