Jeison Yehuda Amihud (Blender Dumbass)
d067b6c64c
These are the UI and studio system part of The implementation
643 lines
29 KiB
Python
643 lines
29 KiB
Python
# THIS FILE IS A PART OF VCStudio
|
|
# PYTHON 3
|
|
|
|
import os
|
|
import datetime
|
|
import threading
|
|
|
|
# GTK module ( Graphical interface
|
|
import gi
|
|
gi.require_version('Gtk', '3.0')
|
|
from gi.repository import Gtk
|
|
from gi.repository import GLib
|
|
from gi.repository import Gdk
|
|
import cairo
|
|
|
|
# Own modules
|
|
from settings import settings
|
|
from settings import talk
|
|
from project_manager import pm_project
|
|
|
|
from studio import analytics
|
|
from studio import studio_nodes
|
|
from studio import studio_dialogs
|
|
from studio import story
|
|
from studio import analytics
|
|
from studio import checklist
|
|
|
|
#UI modules
|
|
from UI import UI_elements
|
|
from UI import UI_color
|
|
|
|
################################################################################
|
|
|
|
# This file is going to hande HISTORY. Both recoring and showing. I suppose in
|
|
# future when I gonna implement Networking Multiuser support. History will be
|
|
# handling some of it too. Will see. ( I might forget about this comment and it's
|
|
# already implemented, who knows. )
|
|
|
|
################################################################################
|
|
|
|
def record(win, filename, task, checklist=[] ):
|
|
|
|
############################################################################
|
|
|
|
# This function will write the history file. Or incase connecting to multiuser
|
|
# will also notify the server of a task being done. This file probably going
|
|
# to change quite usually. Because I might start writting in more or less
|
|
# stuff. So keep an eye on it.
|
|
|
|
# Now since this will touch the multiuser and I can't guarantee that all
|
|
# users will have the same version. The multiuser part of this should insure
|
|
# stable backward compatibility with all verisons of VCStudio.
|
|
|
|
# BUT THERE IS NO WARRANTY! So who knows if exidentaly break something for
|
|
# the old versions. So keeping up to date quite advisable. Or at least keep
|
|
# the same version of VCStudio across all the computers involved in multiuser.
|
|
|
|
############################################################################
|
|
|
|
################### CHECKLIST OF IMPEMENTED DATATYPES ######################
|
|
|
|
# [V] [Openned] # WHEN BLEND FILE IS OPENNED [ settings/oscalls.py ]
|
|
# [V] [Linked] # AS LINKED ASSET INTO BLEND [ studio/studio_shot_linkLayer.py ]
|
|
# [V] [Edited] # WHEN EDITED STORY [ studio/studio_scriptLayer.py ]
|
|
# [V] [Updated] # WHEN CONFIGURED STUFF [ studio/studio_asset_configureLayer.py ]
|
|
# [V] [Added] # WHEN ADDING A BLEND FILE [ settings/oscalls.py ]
|
|
# [V] [Added Asset] # WHEN ADDING AN ASSET [ studio/studio_asset_selectLayer.py ]
|
|
# [V] [Checked] # WHEN [V] IN CHECKLIST [ studio/checklist.py ]
|
|
# [V] [Un-Checked] # WHEN [ ] IN CHECKLIST [ studio/checklist.py ]
|
|
|
|
############################################################################
|
|
|
|
############### PARSING THE URLS ##############
|
|
|
|
print("HISTORY-RAW: ", filename, task, checklist)
|
|
|
|
# Fisrt of all let's remove the project's url
|
|
filename = filename.replace(win.project, "").replace("//", "/")
|
|
|
|
# Now let's find a type
|
|
t = "files"
|
|
item = filename
|
|
if filename.startswith("/rnd"):
|
|
t = "scenes"
|
|
item = filename.replace("/rnd", "")
|
|
filename = filename[filename.rfind("/"):]
|
|
item = item[:item.rfind(filename)]
|
|
|
|
elif filename.startswith("/ast") or filename.startswith("/dev"):
|
|
t = "assets"
|
|
|
|
if filename.startswith("/ast"):
|
|
filename = "[asset_blend]"
|
|
item = item.replace(".blend", "").replace("/ast", "")
|
|
|
|
elif filename.startswith("/dev"):
|
|
f = filename[filename.rfind("/"):]
|
|
item = filename.replace(f, "").replace("/dev", "")
|
|
filename = f
|
|
else:
|
|
filename = filename[filename.rfind("/"):]
|
|
|
|
item = item.replace("//", "/")
|
|
filename = filename.replace("//", "/")
|
|
|
|
# Somebody please look at this. Maybe there is more clever way to record
|
|
# editing of scenes. I don't know.
|
|
if task == "[Edited]":
|
|
item = filename
|
|
|
|
print("HISTORY: ", t, item, filename, task, checklist)
|
|
|
|
|
|
########### WRITING TO THE ANANLYTICS ###########
|
|
|
|
new_date_format = "%Y/%m/%d"
|
|
date = datetime.datetime.strftime(datetime.datetime.today(), new_date_format)
|
|
|
|
seconds_format = "%H:%M:%S"
|
|
time = datetime.datetime.strftime(datetime.datetime.now(), seconds_format)
|
|
|
|
username = win.settings["Username"]
|
|
|
|
# Now since we have our DATE and TIME we can start writing.
|
|
|
|
|
|
if date not in win.analytics["dates"]:
|
|
win.analytics["dates"][date] = {}
|
|
if t not in win.analytics["dates"][date]:
|
|
win.analytics["dates"][date][t] = {}
|
|
if item not in win.analytics["dates"][date][t]:
|
|
win.analytics["dates"][date][t][item] = []
|
|
|
|
# APPENDNING
|
|
if not checklist:
|
|
win.analytics["dates"][date][t][item].append(
|
|
[
|
|
time,
|
|
"history",
|
|
filename,
|
|
task,
|
|
username
|
|
]
|
|
)
|
|
else:
|
|
win.analytics["dates"][date][t][item].append(
|
|
[
|
|
time,
|
|
"history",
|
|
filename,
|
|
task,
|
|
checklist,
|
|
username
|
|
]
|
|
)
|
|
|
|
# REFRASHING
|
|
|
|
analytics.save(win.project, win.analytics)
|
|
win.analytics = analytics.load(win.project)
|
|
|
|
# Multiuser sycning
|
|
win.multiuser["request"] = "analytics"
|
|
|
|
def draw(outlayer, win):
|
|
|
|
x = 10
|
|
y = 70
|
|
width = win.current["w"] / 4 - 20
|
|
height = win.current["h"] - 80
|
|
|
|
|
|
# Now let's make a layer.
|
|
|
|
# Making the layer
|
|
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
|
|
layer = cairo.Context(surface)
|
|
layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
|
|
|
|
# Clip
|
|
UI_elements.roundrect(layer, win,
|
|
0,
|
|
0,
|
|
width,
|
|
height,
|
|
10,
|
|
fill=False)
|
|
layer.clip()
|
|
|
|
# Background
|
|
#UI_color.set(layer, win, "dark_overdrop")
|
|
#layer.rectangle(0,0,width, height)
|
|
#layer.fill()
|
|
|
|
new_date_format = "%Y/%m/%d"
|
|
today = datetime.datetime.strftime(datetime.datetime.today(), new_date_format)
|
|
|
|
if not win.cur:
|
|
UI_elements.text(outlayer, win, "current_date_setting",
|
|
width-100-(width-6*40),
|
|
15,
|
|
(width-6*40),
|
|
40,
|
|
set_text=win.current["date"])
|
|
|
|
if win.text["current_date_setting"]["text"] != win.current["date"]\
|
|
and analytics.ifdate(win.text["current_date_setting"]["text"]):
|
|
def do():
|
|
win.current["date"] = win.text["current_date_setting"]["text"]
|
|
win.textactive = ""
|
|
|
|
UI_elements.roundrect(outlayer, win,
|
|
width-140,
|
|
15,
|
|
40,
|
|
40,
|
|
10,
|
|
button=do,
|
|
icon="ok",
|
|
tip=talk.text("checked"))
|
|
|
|
elif win.current["date"] != today:
|
|
def do():
|
|
win.current["date"] = today
|
|
win.text["current_date_setting"]["text"] = today
|
|
win.textactive = ""
|
|
|
|
UI_elements.roundrect(outlayer, win,
|
|
width-140,
|
|
15,
|
|
40,
|
|
40,
|
|
10,
|
|
button=do,
|
|
icon="cancel",
|
|
tip=talk.text("cancel"))
|
|
|
|
|
|
if "history" not in win.scroll:
|
|
win.scroll["history"] = 0
|
|
|
|
current_Y = 0
|
|
|
|
# OKAY I GUESS SINCE HISTORY IS RELATIVELLY SIMPLE (LOL) I WILL PARSE IT
|
|
# STRAIGHT. Without making a whole lot of parsing. Because with schedules
|
|
# it resulted in some rather hilarious stuff.
|
|
|
|
if "history_selection" not in win.current:
|
|
win.current["history_selection"] = {
|
|
"date":win.current["date"],
|
|
"item":False,
|
|
"user":False
|
|
}
|
|
|
|
dates = win.analytics["dates"]
|
|
|
|
|
|
for date in dates:
|
|
|
|
if not win.cur and date != win.current["date"]:
|
|
continue
|
|
|
|
theday = date
|
|
date = dates[date]
|
|
|
|
# Now let's parse throught it. I guess.
|
|
|
|
for i in ["files", "assets", "scenes"]:
|
|
if i in date:
|
|
for item in date[i]:
|
|
|
|
# This is our individual items. I want to create a folder
|
|
# with USERS inside it. So you could see who done what.
|
|
|
|
|
|
|
|
if win.cur.count("/") > 1:
|
|
tmp = win.cur.replace("/","",1).split("/")
|
|
acur, name = tmp[0], tmp[1]
|
|
else:
|
|
name = win.cur[win.cur.rfind("/")+1:]
|
|
acur = ""
|
|
|
|
if win.cur not in item and win.cur:
|
|
continue
|
|
|
|
|
|
name = item[item.rfind("/")+1:]
|
|
acur = item.replace(name, "").replace("/", "")
|
|
|
|
found = {}
|
|
|
|
for stuff in date[i][item]:
|
|
if stuff[1] == "history":
|
|
if stuff[-1] not in found:
|
|
found[stuff[-1]] = []
|
|
if stuff[3] in ["[Checked]", "[Un-Checked]"]:
|
|
found[stuff[-1]].append([stuff[0],stuff[2],stuff[3],stuff[4]])
|
|
else:
|
|
found[stuff[-1]].append([stuff[0],stuff[2],stuff[3],[]])
|
|
|
|
if found:
|
|
# Now let's output our findings
|
|
UI_color.set(layer, win, "node_background")
|
|
UI_elements.roundrect(layer, win,
|
|
0,
|
|
win.scroll["history"] + current_Y,
|
|
width,
|
|
50,
|
|
10)
|
|
|
|
|
|
|
|
# ICON
|
|
if i == "scenes" and not win.cur:
|
|
|
|
if item.count("/") > 1:
|
|
|
|
UI_elements.image(layer, win,
|
|
"settings/themes/"+win.settings["Theme"]+"/icons/shot.png",
|
|
5, win.scroll["history"] + current_Y+5, 40, 40)
|
|
else:
|
|
|
|
UI_elements.image(layer, win,
|
|
"settings/themes/"+win.settings["Theme"]+"/icons/scene.png",
|
|
5, win.scroll["history"] + current_Y+5, 40, 40)
|
|
elif i == "assets" and not win.cur:
|
|
|
|
if os.path.exists(os.getcwd()+"/settings/themes/"+win.settings["Theme"]+"/icons/"+acur+".png"):
|
|
|
|
UI_elements.image(layer, win,
|
|
"settings/themes/"+win.settings["Theme"]+"/icons/"+acur+".png",
|
|
5, win.scroll["history"] + current_Y+5, 40, 40)
|
|
else:
|
|
|
|
UI_elements.image(layer, win,
|
|
"settings/themes/"+win.settings["Theme"]+"/icons/"+name+".png",
|
|
5, win.scroll["history"] + current_Y+5, 40, 40)
|
|
|
|
name = talk.text(name.replace("/", ""))
|
|
else:
|
|
UI_elements.image(layer, win,
|
|
"settings/themes/"+win.settings["Theme"]+"/icons/history.png",
|
|
5, win.scroll["history"] + current_Y+5, 40, 40)
|
|
|
|
# MAIN TASK
|
|
UI_color.set(layer, win, "text_normal")
|
|
layer.set_font_size(20)
|
|
layer.move_to(
|
|
50,
|
|
win.scroll["history"] + current_Y+30,
|
|
)
|
|
if not win.cur:
|
|
if "chr" not in item and "veh" not in item and "loc" not in item and "obj" not in item and "set" not in item:
|
|
layer.show_text(item.replace("/","",1).replace("/", " | "))
|
|
else:
|
|
layer.show_text(name.replace("project.progress", win.analytics["name"]))
|
|
else:
|
|
layer.show_text(theday)
|
|
# Selection button
|
|
|
|
def do():
|
|
|
|
win.current["history_selection"]["item"] = item
|
|
win.current["history_selection"]["date"] = theday
|
|
win.current["history_selection"]["user"] = False
|
|
|
|
UI_elements.roundrect(layer, win,
|
|
0,
|
|
win.scroll["history"] + current_Y,
|
|
width,
|
|
50,
|
|
10,
|
|
button=do,
|
|
fill=False,
|
|
offset=[x,y])
|
|
layer.stroke()
|
|
|
|
if win.current["history_selection"]["item"] == item\
|
|
and win.current["history_selection"]["date"] == theday:
|
|
|
|
UI_color.set(layer, win, "progress_background")
|
|
UI_elements.roundrect(layer, win,
|
|
0,
|
|
win.scroll["history"] + current_Y,
|
|
width,
|
|
50,
|
|
10,
|
|
fill=False)
|
|
layer.stroke()
|
|
|
|
current_Y = current_Y + 60
|
|
|
|
# It a history is selected you could open up the list
|
|
# of users that worked on a given task.
|
|
|
|
for user in found:
|
|
|
|
UI_color.set(layer, win, "node_background")
|
|
UI_elements.roundrect(layer, win,
|
|
20,
|
|
win.scroll["history"] + current_Y,
|
|
width-20,
|
|
50,
|
|
10)
|
|
|
|
UI_elements.image(layer, win,
|
|
"settings/themes/"+win.settings["Theme"]+"/icons/user.png",
|
|
25, win.scroll["history"] + current_Y+5, 40, 40)
|
|
|
|
# NAME OF THE USER
|
|
|
|
UI_color.set(layer, win, "text_normal")
|
|
layer.set_font_size(20)
|
|
layer.move_to(
|
|
70,
|
|
win.scroll["history"] + current_Y+30,
|
|
)
|
|
layer.show_text(user)
|
|
|
|
# SELECTION BOX
|
|
|
|
def do():
|
|
|
|
win.current["history_selection"]["user"] = user
|
|
|
|
UI_elements.roundrect(layer, win,
|
|
20,
|
|
win.scroll["history"] + current_Y,
|
|
width-20,
|
|
50,
|
|
10,
|
|
button=do,
|
|
fill=False,
|
|
offset=[x,y])
|
|
layer.stroke()
|
|
|
|
# IF THE USER IS SELECTED
|
|
|
|
if win.current["history_selection"]["user"] == user:
|
|
|
|
UI_color.set(layer, win, "progress_background")
|
|
UI_elements.roundrect(layer, win,
|
|
20,
|
|
win.scroll["history"] + current_Y,
|
|
width-20,
|
|
50,
|
|
10,
|
|
fill=False)
|
|
layer.stroke()
|
|
|
|
current_Y = current_Y + 60
|
|
|
|
for stuff in found[user]:
|
|
|
|
UI_color.set(layer, win, "node_background")
|
|
UI_elements.roundrect(layer, win,
|
|
40,
|
|
win.scroll["history"] + current_Y,
|
|
width-40,
|
|
50,
|
|
10)
|
|
|
|
|
|
# Here if both category and the user are
|
|
# selected I need to parse the data of
|
|
# the history to present it to you.
|
|
|
|
operation_icons = {
|
|
"[Openned]":"blender",
|
|
"[Linked]":"file_link",
|
|
"[Edited]":"scene",
|
|
"[Updated]":"update",
|
|
"[Added]":"new",
|
|
"[Added Asset]":"asset_new",
|
|
"[Checked]":"checked",
|
|
"[Un-Checked]":"unchecked"
|
|
}
|
|
|
|
|
|
try:
|
|
icon = operation_icons[stuff[2]]
|
|
except:
|
|
icon = "history"
|
|
|
|
UI_elements.image(layer, win,
|
|
"settings/themes/"+win.settings["Theme"]+"/icons/"+icon+".png",
|
|
45, win.scroll["history"] + current_Y+5, 40, 40)
|
|
|
|
# Next will be to add some text about
|
|
# what exactly was done here. And I know
|
|
# that we are already super inside. I
|
|
# mean indentation. If somebody want's
|
|
# to reduse a few spaces. They are
|
|
# welcome to try.
|
|
|
|
# Let's split the the history entries to
|
|
# 2 types. Checklists and everything else.
|
|
|
|
# CHECKLISTS
|
|
if "Checked" in stuff[2]:
|
|
|
|
# Let's get task and url
|
|
try:
|
|
task = stuff[-1][-1]
|
|
fullurl = ""
|
|
for e in stuff[-1][:-1]:
|
|
fullurl = fullurl+e+" > "
|
|
except:
|
|
tasl = ""
|
|
fullurl = ""
|
|
|
|
|
|
# MAIN TASK
|
|
UI_color.set(layer, win, "text_normal")
|
|
layer.set_font_size(20)
|
|
layer.move_to(
|
|
80,
|
|
win.scroll["history"] + current_Y+24,
|
|
)
|
|
layer.show_text(task)
|
|
|
|
|
|
# TASK URL INSIDE THE CHECKLIST
|
|
|
|
if fullurl:
|
|
UI_color.set(layer, win, "text_normal")
|
|
layer.set_font_size(10)
|
|
layer.move_to(
|
|
90+len(task)*12,
|
|
win.scroll["history"] + current_Y+24,
|
|
)
|
|
layer.show_text(fullurl)
|
|
|
|
else:
|
|
# If it's not a checklist. I just want
|
|
# to output what it is and what action
|
|
# happened to it.
|
|
|
|
task = stuff[1].replace("/", " ")
|
|
if task == "[asset_blend]":
|
|
task = " Asset Blend File"
|
|
|
|
# WHAT
|
|
UI_color.set(layer, win, "text_normal")
|
|
layer.set_font_size(20)
|
|
layer.move_to(
|
|
80,
|
|
win.scroll["history"] + current_Y+24,
|
|
)
|
|
layer.show_text(task)
|
|
|
|
|
|
# WHAT WAS DONE
|
|
|
|
UI_color.set(layer, win, "text_normal")
|
|
layer.set_font_size(10)
|
|
layer.move_to(
|
|
90+len(task)*12,
|
|
win.scroll["history"] + current_Y+24,
|
|
)
|
|
layer.show_text(stuff[2])
|
|
|
|
UI_color.set(layer, win, "text_normal")
|
|
layer.set_font_size(15)
|
|
layer.move_to(
|
|
92,
|
|
win.scroll["history"] + current_Y+41,
|
|
)
|
|
layer.show_text(stuff[0])
|
|
|
|
# Okay and the last thing. The button of
|
|
# when it's clicked. For example to get
|
|
# to the item. Or to find the task in
|
|
# the checklist. For what ever reason.
|
|
|
|
def do():
|
|
|
|
# Let's make the auto-selecting of
|
|
# the checklists. OMG I really have
|
|
# no space left to type. OMG. What
|
|
# is this.
|
|
|
|
if "Checked" in stuff[2]:
|
|
|
|
# AAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
|
# How am I supposed to tell thi-
|
|
# ngs? OMG.
|
|
|
|
if "schedule_task_selected" not in win.current:
|
|
win.current["schedule_task_selected"] = []
|
|
|
|
win.current["schedule_task_selected"] = \
|
|
[[[stuff[0], "schedule", stuff[1], stuff[2], stuff[3]], 0], item]
|
|
|
|
# Now since we did this weird
|
|
# what ever the mack. Let's make so
|
|
# it's going to the asset.
|
|
|
|
if i == "assets":
|
|
goto = "assets"
|
|
elif i == "scenes":
|
|
goto = "script"
|
|
else:
|
|
goto = "analytics"
|
|
|
|
win.url = goto
|
|
win.cur = item
|
|
win.current["asset_left_panel"] = "history"
|
|
|
|
if stuff[2] != "[Added Asset]":
|
|
|
|
UI_elements.roundrect(layer, win,
|
|
40,
|
|
win.scroll["history"] + current_Y,
|
|
width-40,
|
|
50,
|
|
10,
|
|
button=do,
|
|
fill=False,
|
|
offset=[x,y])
|
|
layer.stroke()
|
|
|
|
|
|
current_Y = current_Y + 60
|
|
else:
|
|
current_Y = current_Y + 60
|
|
else:
|
|
current_Y = current_Y + 60
|
|
|
|
# Outputting the layer
|
|
outlayer.set_source_surface(surface, x, y)
|
|
outlayer.paint()
|
|
|
|
# Scroll
|
|
UI_elements.scroll_area(outlayer, win, "history",
|
|
x+0,
|
|
y+50,
|
|
width,
|
|
height-50,
|
|
current_Y,
|
|
bar=True,
|
|
mmb=True)
|