Blender-Pipeline/studio/studio_shot_linkLayer.py

664 lines
21 KiB
Python

# THIS FILE IS A PART OF VCStudio
# PYTHON 3
import os
from subprocess import *
# 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 oscalls
from settings import talk
from settings import fileformats
from project_manager import pm_project
#UI modules
from UI import UI_elements
from UI import UI_color
# story
from studio import story
from studio import analytics
from studio import history
from studio import studio_dialogs
from studio import history
def link_from_script(win):
#########################################################################
# This file is here because there are multiple places in the window where
# I want to link assets based on what's written in the text of the script.
# So this file will basically parse the text and add the assets it finds
# linked to the link list.
#########################################################################
print("Reading from the scene")
# Fist we need to know what scene and shot are we.
if win.cur.count("/") > 1:
tmp = win.cur.replace("/","",1).split("/")
scene, shotis = tmp[0], tmp[1]
else:
scene = win.cur[win.cur.rfind("/")+1:]
shotis = ""
print(scene, shotis)
# Now let's read through the scene.
for shot in win.story["scenes"][scene]["shots"]:
# Ignore the non shots.
if shot[0] != "shot_block":
continue
# Ingore all other shots.
if shot[1] != shotis:
continue
# Let's read the shot
for asset in shot[-1]:
# Ignore text
if asset[0] == "text":
continue
# It's it's a staight up link. Just add it.
if asset[0] == "link" and "/dev"+asset[1] not in win.current["linking_asset_data"]["assets"]:
win.current["linking_asset_data"]["assets"].append("/dev"+asset[1])
# It's a link in a frase.
if asset[0] == "frase" and asset[1][0] == "link" and "/dev"+asset[1][1] not in win.current["linking_asset_data"]["assets"]:
win.current["linking_asset_data"]["assets"].append("/dev"+asset[1][1])
def layer(win, call):
# Making the layer
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, win.current['w'],
win.current['h'])
layer = cairo.Context(surface)
#text setting
layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
UI_color.set(layer, win, "dark_overdrop")
layer.rectangle(
0,
0,
win.current["w"],
win.current["h"],
)
layer.fill()
UI_color.set(layer, win, "node_background")
UI_elements.roundrect(layer, win,
40,
40,
win.current["w"]-80,
win.current["h"]-80,
10)
##############################################################################
# This file is configuring and lanunching the linking into blend files. But it's
# not the only file in the chain of files that is responsible for this operation.
# For example the linking it self does studio/bpy_do_linking.py that is running
# inside blender and communicates with this file for the UI side of things.
##############################################################################
# Okay
if not win.current["linking_asset_data"]["fraction"]:
def do():
# Before we start let's record the history for it.
history.record(win, win.project+win.current["linking_asset_data"]["linking_to"], "[Linked]")
if not win.current["linking_asset_data"]["process"]:
# So this is an apply button. I know it's a little not in the right
# order. So please read the rest of the file to see what it does.
# So now when a person clicks okay. We first need to save the
# autolink.data.
datafile = open(win.project+"/rnd"+win.cur+"/extra/autolink.data", "w")
for asset in win.current["linking_asset_data"]["assets"]:
datafile.write("Link : "+asset+"\n")
# Also let's write the mode. Because it's important.
datafile.write("Mode : "+win.current["linking_asset_data"]["mode"]+"\n")
datafile.close()
# Okay... now that we have the file. We can start the process of linking
# Which will be done using Blender. So let's get blender's url
blenderpath = oscalls.get_current_blender(win)
# Now here is the fun part. We gonna start a background process
# open there blender and will start linking stuff.
win.current["linking_asset_data"]["process"] = Popen(['stdbuf', '-o0', blenderpath, \
"-b", win.project+win.current["linking_asset_data"]["linking_to"], \
"-P", os.getcwd()+"/studio/bpy_do_linking.py"],\
stdout=PIPE, universal_newlines=True)
win.current["linking_asset_data"]["fraction"] = 0.01
# Hell of a command isn't it.
UI_elements.roundrect(layer, win,
win.current["w"]-120,
win.current["h"]-80,
40,
40,
10,
button=do,
icon="ok",
tip=talk.text("checked"),
url="asset_link")
# Documentation entry
def do():
def after(win, var):
pass
studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_link_assets"))
UI_elements.roundrect(layer, win,
300,
50,
40,
40,
10,
do,
"question")
# CANCEl
if not win.current["linking_asset_data"]["process"]:
def do():
if not win.current["linking_asset_data"]["process"]:
win.current["calls"][call]["var"] = False
win.assets = {}
UI_elements.roundrect(layer, win,
win.current["w"]-80,
win.current["h"]-80,
40,
40,
10,
button=do,
icon="cancel",
tip=talk.text("cancel"),
url="asset_link")
# Short cut ESC
if 65307 in win.current["keys"] and not win.textactive:
do()
# Now let's prepare the ground for the next part.
# Before we do anything we need to make sure that there is an autolink.data
# file in the extra folder of the shot. But since this is a system that is
# designed to be very simple on the user side. There could not even be the
# extra folder. So...
try:
os.mkdir(win.project+"/rnd"+win.cur+"/extra")
except:
pass
# Next thing will be to see if there is a file called autolink.data. And if
# yes read from it. We are going to load the data into the memory.
# So if there is nothing yet loaded let's try loading it from file. Try
# because there nothing quarantees that the file exists. If file does not
# exists we gonna populate it with assets from the shot. As marked in the
# scene the user wrote. But it's going to be a separate function since I
# want to make a button to do it.
if not win.current["linking_asset_data"]["assets"] and not win.current["linking_asset_data"]["read"]:
win.current["linking_asset_data"]["read"] = True
try:
datafile = open(win.project+"/rnd"+win.cur+"/extra/autolink.data")
datafile = datafile.read()
datafile = datafile.split("\n")
for line in datafile:
if line.startswith("Link : "):
asset = line[7:]
win.current["linking_asset_data"]["assets"].append(asset)
except:
# So if it fails to load the file. Probably because it's not created
# yet. Let's load from the story. What's so bad about it?
link_from_script(win)
# Now keep in mind that to help the script that runs inside blender all
# assets have /dev/ in their folder. So to reach /ast/ blend file we will
# need to parse the link. It's not hard just reminding it here.
# IK everything is all over the place in the file but...
# If there is a process. We should be able to see what it does.
# Draw like a litte progress bar and stuff
if win.current["linking_asset_data"]["process"]:
# Basically during the execution of the linking. I want to read the
# output and give the user some feedback of what is going on.
currentline = win.current["linking_asset_data"]["process"].stdout.readline()[:-1]
# In the bpy_do_linking.py file there is an instruction to write
# out a fraction of a current operation into terminal. Which is
# piped into the VCStudio and I can read it. So we can use it
# to draw a progress bar.
if currentline.startswith("FRACTION:"):
try:
fraction = float(currentline.split(":")[1])
win.current["linking_asset_data"]["fraction"] = fraction
except:
pass
if currentline == "FINISHED":
win.current["linking_asset_data"]["process"] = False
win.current["linking_asset_data"]["fraction"] = 1
# The prgoress bar:
if win.current["linking_asset_data"]["fraction"]:
fraction = win.current["linking_asset_data"]["fraction"]
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
70,
win.current["h"]-75,
(win.current["w"]-220),
0,
7)
UI_color.set(layer, win, "progress_active")
UI_elements.roundrect(layer, win,
70,
win.current["h"]-75,
(win.current["w"]-220)*fraction,
0,
7)
# This file will fuel of nightmares. Okay. So while developping it I realzed
# one little problem. Overrides are kind a still buggy. Tho it's good to
# have them for future releases. And most of rigs work just fine.
# The thing that I want to do is to give the user options. Let's say their
# rig is broken when trying Library- Overrides but it's okay if you do
# legacy Proxy thing. So why not give the user the option of doing both.
# And also sometimes you just want to link and decide later what you do with
# your rig.
# So I gonna make 3 icons. 3 modes so to speak. LINK, OVERRIDE, PROXY.
linkoptions = {
"link":"link_mode_link",
"override":"link_mode_overrides", # Icon / Mode : Tooltip
"proxy":"link_mode_proxy"
}
for num, mode in enumerate(linkoptions):
if win.current["linking_asset_data"]["mode"] == mode:
UI_color.set(layer, win, "progress_time")
UI_elements.roundrect(layer, win,
50+(40*num),
50,
40,
40,
10)
def do():
win.current["linking_asset_data"]["mode"] = mode
UI_elements.roundrect(layer, win,
50+(40*num),
50,
40,
40,
10,
do,
mode,
tip=talk.text(linkoptions[mode]))
# Let's add a button that adds assets based on what's written in the script.
def do():
link_from_script(win)
UI_elements.roundrect(layer, win,
200,
50,
40,
40,
10,
do,
"scene_new",
tip=talk.text("link_add_from_scene"))
if not os.path.exists(win.project+win.current["linking_asset_data"]["selected"]+"/autolink.data")\
or not os.path.exists(win.project+win.current["linking_asset_data"]["selected"].replace("/dev/","/ast/")+".blend"):
def do():
def after(win, var):
print(var)
studio_dialogs.asset_configure(win, "configuring_asset", after,
win.current["linking_asset_data"]["selected"].replace("/dev",""))
UI_elements.roundrect(layer, win,
250,
50,
40,
40,
10,
button=do,
icon="link_configure")
# That's it. Now we are clipping and drawing the assets them selves.
UI_elements.roundrect(layer, win,
50,
100,
win.current["w"]-100,
win.current["h"]-200,
10,
fill=False)
layer.clip()
tileX = 70
current_Y = 0
if "asset_link" not in win.scroll:
win.scroll["asset_link"] = 0
#############################
# Let's draw the list of assets that the user chose.
for num, asset in enumerate(win.current["linking_asset_data"]["assets"]):
cur, name = asset.split("/")[2], asset.split("/")[3]
if int(current_Y + win.scroll["asset_link"] + 100) in range(0-100, win.current["h"]):
# Making the layer
nodesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 170, 200)
node = cairo.Context(nodesurface)
node.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
UI_elements.roundrect(node, win,
0,
0,
170,
200,
10,
fill=False)
node.clip()
# Background
UI_color.set(node, win, "dark_overdrop")
node.rectangle(0,0,170, 200)
node.fill()
# Banner
UI_color.set(node, win, "node_asset")
# Now the banner will be node_asset color if the asset is configured
# but it could be not configured. Or not even have the asset blend.
# so we need to quickly check both. And if any of them missing. Make
# the banner a node_badfile color.
warning = ""
if not os.path.exists(win.project+asset+"/autolink.data")\
or not os.path.exists(win.project+asset.replace("/dev/","/ast/")+".blend"):
UI_color.set(node, win, "node_badfile")
warning = "\n"+talk.text("link_asset_not_configured")
# Let's do even cooler. And let's draw a thing arround the asset.
node.set_line_width(10)
UI_elements.roundrect(node, win,
0,
0,
170,
200,
10,
fill=False)
node.stroke()
node.set_line_width(2)
# Even cooler. Let's put a configuration button right into here
node.rectangle(0,0,170, 20)
node.fill()
# Outputting the layer
layer.set_source_surface(nodesurface,
tileX-10,
current_Y + win.scroll["asset_link"] + 120)
layer.paint()
# Previes image
if os.path.exists(win.project+asset+"/renders/Preview.png"):
UI_elements.image(layer, win, win.project+asset+"/renders/Preview.png",
tileX,
current_Y + win.scroll["asset_link"] + 140,
150,
150)
elif os.path.exists(win.project+asset+"/renders/Preview.jpg"):
UI_elements.image(layer, win, win.project+asset+"/renders/Preview.jpg",
tileX,
current_Y + win.scroll["asset_link"] + 140,
150,
150)
else:
UI_elements.image(layer, win, "settings/themes/"+win.settings["Theme"]+"/icons/"+cur+".png",
tileX+55,
current_Y + win.scroll["asset_link"] + 150+55,
150,
150)
UI_color.set(layer, win, "text_normal")
layer.set_font_size(12)
layer.move_to(tileX,
current_Y + win.scroll["asset_link"] + 135)
layer.show_text(name[:22])
# Let's make a selection of an asset so you could delete one. Or
# do other stuff. Maybe.
if win.current["linking_asset_data"]["selected"] == asset:
layer.set_line_width(4)
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
tileX-10,
current_Y + win.scroll["asset_link"] + 120,
170,
200,
10,
fill=False)
layer.stroke()
layer.set_line_width(2)
# And the delete key.
if 65535 in win.current["keys"]:
try:
win.current["linking_asset_data"]["assets"].remove(asset)
except:
pass
win.current["keys"] = []
# Button to activate it
def do():
win.current["linking_asset_data"]["selected"] = asset
layer.set_line_width(4)
UI_elements.roundrect(layer, win,
tileX-10,
current_Y + win.scroll["asset_link"] + 120,
170,
200,
10,
button=do,
tip=talk.text(cur)+": "+name+warning,
fill=False,
clip=[
50,
100,
win.current["w"]-100,
win.current["h"]-200
],
url="asset_link")
layer.stroke()
layer.set_line_width(2)
tileX += 200
if tileX > win.current["w"]-220:
tileX = 70
current_Y += 230
# Now here I want to put the add new one button. So you could add assets
# to the stuff manually.
def do():
# Now this is going to be wild. Because I'm going to run a dialog with
# in a dialog. And I hope that it's going to work.
def after(win, var):
# Funny that it's actually working.
# Tho there is a little rub. If you press Cancel there is a bug that
# makes nothing unusable. so...
try:
del win.current["calls"]["link_add_asset_select"]
except:
pass
if var and "/dev"+var not in win.current["linking_asset_data"]["assets"]:
win.current["linking_asset_data"]["assets"].append("/dev"+var)
# So basically I want to add one only if there is not one already.
# And then select it. So if the use gets which one is added or
# whether it was already there.
win.current["linking_asset_data"]["selected"] = "/dev"+var
# And since it could allight with the add button. Let's clear the
# click
win.current["LMB"] = False
win.previous["LMB"] = False
studio_dialogs.asset_select(win, "link_add_asset_select", after)
UI_elements.roundrect(layer, win,
tileX-10,
current_Y + win.scroll["asset_link"] + 120,
170,
200,
10,
button=do,
url="asset_link")
UI_color.set(layer, win, "progress_background")
UI_elements.roundrect(layer, win,
tileX-10,
current_Y + win.scroll["asset_link"] + 120,
170,
200,
10,
fill=False)
layer.stroke()
UI_elements.image(layer, win,
"settings/themes/"+win.settings["Theme"]+"/icons/asset_new.png",
tileX+55,
current_Y + win.scroll["asset_link"] + 200,
40, 40)
#############################
current_Y += 230
UI_elements.scroll_area(layer, win, "asset_link",
50,
100,
win.current["w"]-100,
win.current["h"]-200,
current_Y,
bar=True,
mmb=True,
url="asset_link",
strenght=130
)
return surface