# THIS FILE IS A PART OF VCStudio # PYTHON 3 # This a console project manager. import os # 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 settings import fileformats from settings import oscalls 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 checklist from studio import analytics from studio import studio_dialogs from studio import schedule from studio import history def layer(win): # 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, win.current["w"]/4, 150, win.current["w"]/2, win.current["h"]-190, 10) ############################################################################ # This file will be showing stuff about an asset. It's almost like a folder # / folders of the asset. But with some extendet functionality. Stuff that # will be on screen will include. # Progress Bar # Checklist # Scenes list in which the asset is mentioned # History / Schedules # It's similar to the old Blender Organizer's Asset thing. But a bit more # modern looking. I would say. ############################################################################ # before we start I want to make a little safe mechanism. Just in case the # win.cur is not written properly. # Parsing the cur to get name and type name = win.cur[win.cur.rfind("/")+1:] acur = win.cur.replace(name, "").replace("/", "") if not os.path.exists(win.project+"/dev/"+win.cur) or not win.cur: def select_character(win, var): if var: win.url = "assets" win.cur = var else: win.url = "story_editor" if not acur: acur = "chr" studio_dialogs.asset_select(win, "select_asset_check", select_character, force=True, cur=acur, SEARCH=name) if not "asset_cur_folder" in win.current: win.current["asset_cur_folder"] = "blender" ####### MIDDLE PANNEL ####### def do(): oscalls.Open(win.project+"/dev"+win.cur) UI_elements.roundrect(layer, win, win.current["w"]/4+20, 350, 40, 40, 10, do, "folder") folds = { "blender":"blend_files_folder", "idea":"reference_folder", "texture":"tex_folder", "render":"renders_folder" } for num, cur in enumerate(folds): if win.current["asset_cur_folder"] == cur: UI_color.set(layer, win, "progress_time") UI_elements.roundrect(layer, win, win.current["w"]/4+150+(40*num), 350, 40, 40, 10) def do(): win.current["asset_cur_folder"] = cur UI_elements.roundrect(layer, win, win.current["w"]/4+150+(40*num), 350, 40, 40, 10, do, cur, tip=talk.text(folds[cur])) # Preview # Making the layer nodesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 300, 300) node = cairo.Context(nodesurface) node.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) UI_elements.roundrect(node, win, 0, 0, 300, 300, 10, fill=False) node.clip() if os.path.exists(win.project+"/dev"+win.cur+"/renders/Preview.png"): UI_elements.image(node, win, win.project+"/dev"+win.cur+"/renders/Preview.png", 0, 0, 300, 300, cell="big_asset_previews") elif os.path.exists(win.project+"/dev"+win.cur+"/renders/Preview.jpg"): UI_elements.image(node, win, win.project+"/dev"+win.cur+"/renders/Preview.jpg", 0, 0, 300, 300, cell="big_asset_previews") else: UI_color.set(node, win, "dark_overdrop") node.rectangle(0,0,300, 300) node.fill() UI_elements.image(node, win, "settings/themes/"+win.settings["Theme"]+"/icons/"+acur+".png", 130, 130, 40, 40) def do(): def after(win, var): win.current["asset_file_selected"] = "" win.images = {} if var: for t in fileformats.images: if var.endswith(t): oscalls.copy_file( win, var, "/dev"+win.cur+"/renders/", "Preview.png") break studio_dialogs.file_select(win, name+"_preview", after, force=True, IMAGE=True, BLEND=False, VIDEO=True, FILE=False, CHR=True, VEH=True, LOC=True, OBJ=True, RND=False, FOLDER=False, SEARCH=win.cur+" renders") UI_elements.roundrect(node, win, 2, 2, 296, 296, 10, button=do, fill=False, offset=[ win.current["w"]/4+20, 20 ] ) node.stroke() # Outputting the layer layer.set_source_surface(nodesurface, win.current["w"]/4+20, 20) layer.paint() # Name of the asset UI_elements.image(layer, win, "settings/themes/"+win.settings["Theme"]+"/icons/"+acur+".png", win.current["w"]/4+360, 210, 40, 40) UI_color.set(layer, win, "text_normal") layer.set_font_size(30) layer.move_to(win.current["w"]/4+410,240) layer.show_text(name) # Fraction fraction = story.get_asset_data(win, win.cur)["fraction"] UI_color.set(layer, win, "progress_background") UI_elements.roundrect(layer, win, win.current["w"]/4+350, 270, (win.current["w"]/2-370), 0, 10) UI_color.set(layer, win, "progress_active") UI_elements.roundrect(layer, win, win.current["w"]/4+350, 270, (win.current["w"]/2-370)*fraction, 0, 10) # Before search. On the other side when you have done the asset there will # be a little configure icon. This is configuration for linking. Creating # the /ast/ blend.file and autolink.data file. So the linker could link # the files into the animation scene. # # See: # studio/studio_shot_linkLayer.py # studio/bpy_do_linking.py # studio/studio_asset_configureLayer.py def do(): def after(win, var): print(var) studio_dialogs.asset_configure(win, "configuring_asset", after, win.cur) UI_elements.roundrect(layer, win, win.current["w"]/4*3-60, 210, 40, 40, 10, button=do, icon="link_configure") # Search UI_elements.image(layer, win, "settings/themes/"\ +win.settings["Theme"]+"/icons/search.png", win.current["w"]-280-win.current["w"]/4, 350, 40, 40) UI_elements.text(layer, win, "in_asset", win.current["w"]-240-win.current["w"]/4, 350, 220, 40) # FILES # In case the user made the folder manually. try: os.mkdir(win.project+"/dev/"+win.cur+"/renders") os.mkdir(win.project+"/dev/"+win.cur+"/reference") os.mkdir(win.project+"/dev/"+win.cur+"/tex") except: pass # Now let's prepare the frame width = win.current["w"]/2- 40 height = win.current["h"] - 500 # Making the layer nodesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height)) node = cairo.Context(nodesurface) node.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) UI_elements.roundrect(node, win, 0, 0, width, height, 10, fill=False) node.clip() # Now let's prepare the scrooler tileX = 20 current_Y = 10 if "assets" not in win.scroll: win.scroll["assets"] = 0 ########################### # Let's get the list of the files first. files = [] newcreate = win.text["in_asset"]["text"].replace("/","_").replace(" ", "_")\ .replace('"',"_").replace("(","_").replace(")","_").replace("'","_")\ .replace("[","_").replace("]","_").replace("{","_").replace("}","_") foundblend = True found_in = False if win.current["asset_cur_folder"] == "blender": foundblend = False # Let's see if an asset blend file exists. if os.path.exists(win.project+"/ast"+win.cur+".blend") and not newcreate: files.append(["/ast"+win.cur+".blend", "node_asset"]) foundblend = True for f in sorted(os.listdir(win.project+"/dev"+win.cur)): if not os.path.isdir(win.project+"/dev"+win.cur+"/"+f): # This is for only the blend files. So let's filter the out of all # the noise. For all files there is a folder icon. if newcreate == f: found_in = True if newcreate and newcreate.lower() not in f.lower(): continue if f.endswith(".blend"): files.append(["/dev"+win.cur+"/"+f, "node_blendfile"]) foundblend = True else: # This all the rest of the files. if win.current["asset_cur_folder"] == "idea": fl = "reference" elif win.current["asset_cur_folder"] == "texture": fl = "tex" elif win.current["asset_cur_folder"] == "render": fl = "renders" for f in sorted(os.listdir(win.project+"/dev"+win.cur+"/"+fl)): if not os.path.isdir(win.project+"/dev"+win.cur+"/"+fl+"/"+f): if newcreate == f: found_in = True if newcreate and newcreate.lower() not in f.lower(): continue # Now it has to be image or video files. For the rest you have # the folder icon. for t in fileformats.images: if f.endswith(t): files.append(["/dev"+win.cur+"/"+fl+"/"+f, "node_imagefile"]) for t in fileformats.videos: if f.endswith(t): files.append(["/dev"+win.cur+"/"+fl+"/"+f, "node_videofile"]) # Let's make sure that we have at least some blendfile there. if not foundblend and not newcreate: oscalls.copy_file( win, os.getcwd()+"/new_file/"+acur+".blend", "/dev"+win.cur+"/", name+".blend") if "asset_file_selected" not in win.current: win.current["asset_file_selected"] = "" for filename, thecoloris in files: if int(current_Y + win.scroll["assets"]) in range(0-200, height): # Making the layer node2surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 170, 200) node2 = cairo.Context(node2surface) node2.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) UI_elements.roundrect(node2, win, 0, 0, 170, 200, 10, fill=False) node2.clip() # Background UI_color.set(node2, win, "dark_overdrop") node2.rectangle(0,0,170, 200) node2.fill() # Banner UI_color.set(node2, win, thecoloris) node2.rectangle(0,0,170, 20) node2.fill() # Outputting the layer node.set_source_surface(node2surface, tileX-10, current_Y + win.scroll["assets"] ) node.paint() UI_elements.image(node, win, win.project+filename, tileX, current_Y + win.scroll["assets"] + 30, 150, 150) UI_color.set(node, win, "text_normal") node.set_font_size(12) node.move_to(tileX, current_Y + win.scroll["assets"]+15) node.show_text(filename[filename.rfind("/")+1:][:22]) # If selected node.set_line_width(4) if win.current["asset_file_selected"] == win.project+filename: UI_color.set(node, win, "progress_background") UI_elements.roundrect(node, win, tileX-10, current_Y + win.scroll["assets"], 170, 200, 10, fill=False) node.stroke() # Button to activate it def do(): if win.current["asset_file_selected"] != win.project+filename: win.current["asset_file_selected"] = win.project+filename else: oscalls.file_open(win, win.project+filename) UI_elements.roundrect(node, win, tileX-10, current_Y + win.scroll["assets"], 170, 200, 10, button=do, tip=filename, fill=False, clip=[ win.current["w"]/4+20, 400, width, height, ], offset=[ win.current["w"]/4+20, 400]) node.stroke() node.set_line_width(2) tileX += 200 if tileX > width-180: tileX = 20 current_Y += 230 ########################### # Now i'd like to make things addable. For now if it's a blends selected # there will an option to add new one and an option to copy. While other # files will have an option of only copy. (At least for now. Untill I # figure out what new images will it give.) if not found_in and newcreate: if win.current["asset_cur_folder"] == "blender"\ and not newcreate.endswith(".blend"): newcreate = newcreate+".blend" def do(): def after(win, var): newcreate = win.text["in_asset"]["text"].replace("/","_").replace(" ", "_")\ .replace('"',"_").replace("(","_").replace(")","_").replace("'","_")\ .replace("[","_").replace("]","_").replace("{","_").replace("}","_") win.text["in_asset"]["text"] = "" win.current["asset_file_selected"] = "" win.images = {} if var: if win.current["asset_cur_folder"] == "blender"\ and var.endswith(".blend"): if not newcreate.endswith(".blend"): newcreate = newcreate+".blend" oscalls.copy_file( win, var, "/dev"+win.cur+"/", newcreate) else: # In case the copying file is not a blend file. Let's # make sure that it's infect an image. for t in fileformats.images: if var.endswith(t): if not newcreate.endswith(t): newcreate = newcreate + "." + t if win.current["asset_cur_folder"] == "idea": fl = "reference" elif win.current["asset_cur_folder"] == "texture": fl = "tex" elif win.current["asset_cur_folder"] == "render": fl = "renders" oscalls.copy_file( win, var, "/dev"+win.cur+"/"+fl+"/", newcreate) break if win.current["asset_cur_folder"] == "blender": studio_dialogs.file_select(win, name+"_blends", after, force=True, IMAGE=False, BLEND=True, VIDEO=False, FILE=False, CHR=True, VEH=True, LOC=True, OBJ=True, RND=False, FOLDER=False) elif win.current["asset_cur_folder"] == "idea": studio_dialogs.file_select(win, name+"_reference", after, force=True, IMAGE=True, BLEND=False, VIDEO=True, FILE=False, CHR=True, VEH=True, LOC=True, OBJ=True, RND=False, FOLDER=False, SEARCH="reference") elif win.current["asset_cur_folder"] == "texture": studio_dialogs.file_select(win, name+"_reference", after, force=True, IMAGE=True, BLEND=False, VIDEO=True, FILE=False, CHR=True, VEH=True, LOC=True, OBJ=True, RND=False, FOLDER=False, SEARCH="tex") elif win.current["asset_cur_folder"] == "render": studio_dialogs.file_select(win, name+"_reference", after, force=True, IMAGE=True, BLEND=False, VIDEO=True, FILE=False, CHR=True, VEH=True, LOC=True, OBJ=True, RND=False, FOLDER=False, SEARCH="renders") UI_elements.roundrect(node, win, tileX-10, current_Y + win.scroll["assets"], 170, 200, 10, button=do, tip=talk.text("copy_file_as")+" "+newcreate, clip=[ win.current["w"]/4+20, 400, width, height, ], offset=[ win.current["w"]/4+20, 400]) UI_color.set(node, win, "progress_background") UI_elements.roundrect(node, win, tileX-10, current_Y + win.scroll["assets"], 170, 200, 10, fill=False) node.stroke() UI_elements.image(node, win, "settings/themes/"+win.settings["Theme"]+"/icons/copy_file.png", tileX+55, current_Y + win.scroll["assets"] + 70, 40, 40) UI_color.set(node, win, "text_normal") node.set_font_size(12) node.move_to(tileX+75-len(newcreate)*4, current_Y + win.scroll["assets"]+150) node.show_text(newcreate) tileX += 200 if tileX > width-180: tileX = 20 current_Y += 230 if not found_in and newcreate and win.current["asset_cur_folder"] == "blender": if not newcreate.endswith(".blend"): newcreate = newcreate+".blend" def do(): oscalls.copy_file( win, os.getcwd()+"/new_file/"+acur+".blend", "/dev"+win.cur+"/", newcreate) win.text["in_asset"]["text"] = "" win.current["asset_file_selected"] = "" win.images = {} UI_elements.roundrect(node, win, tileX-10, current_Y + win.scroll["assets"], 170, 200, 10, button=do, tip=talk.text("create_new_blend_file")+" "+newcreate, clip=[ win.current["w"]/4+20, 400, width, height, ], offset=[ win.current["w"]/4+20, 400]) UI_color.set(node, win, "progress_background") UI_elements.roundrect(node, win, tileX-10, current_Y + win.scroll["assets"], 170, 200, 10, fill=False) node.stroke() UI_elements.image(node, win, "settings/themes/"+win.settings["Theme"]+"/icons/new_file.png", tileX+55, current_Y + win.scroll["assets"] + 70, 40, 40) UI_color.set(node, win, "text_normal") node.set_font_size(12) node.move_to(tileX+75-len(newcreate)*4, current_Y + win.scroll["assets"]+150) node.show_text(newcreate) current_Y += 230 UI_elements.scroll_area(layer, win, "assets", win.current["w"]/4+20, 400, width, height, current_Y, bar=True, mmb=True, url="assets") # Outputting the layer layer.set_source_surface(nodesurface, win.current["w"]/4+20, 400) layer.paint() ############## CHECKLIST ################ if os.path.exists(win.project+"/dev/"+win.cur+"/asset.progress"): checklist.draw(layer, win, win.project+"/dev/"+win.cur+"/asset.progress", back=win.url) else: # If asset.progress does not exist in the folder it will try to create. # one. The only problem with now is language support. I have some ideas # I will need to work on it. But later. oscalls.copy_file( win, os.getcwd()+"/new_file/"+acur+".progress", "/dev"+win.cur+"/", "asset.progress") ############## LEFT PANEL ################ # Here on the left panel I want to have 3 things. Which is already more then # the legacy organizer. But who are we deceiving? This is way cooler then # the legacy organizer. leftpanellist = ["scene", "schedule", "history"] # Using the names of the icons. # We need to choose the correct category based smartly on the project's # current progress. Or at least on the current progress of this asset. if "asset_left_panel" not in win.current: if fraction == 1.0: # If the asset is done win.current["asset_left_panel"] = "scene" # Then list scenes else: # Other win.current["asset_left_panel"] = "schedule" # List schedules # A little banner. UI_color.set(layer, win, "node_background") UI_elements.roundrect(layer, win, 10, 10, win.current["w"]/4-20, 50, 10) for num, thing in enumerate(leftpanellist): if win.current["asset_left_panel"] == thing: UI_color.set(layer, win, "progress_time") UI_elements.roundrect(layer, win, 20+(40*num), 15, 40, 40, 10) def do(): win.current["asset_left_panel"] = thing UI_elements.roundrect(layer, win, 20+(40*num), 15, 40, 40, 10, do, thing) ### SCHEDULES ### if win.current["asset_left_panel"] == "schedule": schedule.draw(layer, win) ### HISTORY ### if win.current["asset_left_panel"] == "history": history.draw(layer, win) ### SCENES ### # CANCEl def do(): win.url = "story_editor" win.assets = {} win.current["asset_file_selected"] = "" UI_elements.roundrect(layer, win, win.current["w"]-40-win.current["w"]/4, win.current["h"]-80, 40, 40, 10, button=do, icon="cancel", tip=talk.text("cancel")) # Short cut ESC if 65307 in win.current["keys"] and not win.textactive: do() if win.current["asset_left_panel"] == "scene": # Here I want to parse the story data in pusuit of all the scenes that # have the asset. I think I'm going to make it per shot based. Like # you have the scenes. And you can open them to enter shots. Maybe with # a tiny exserp from the story. if "asset_scenes" not in win.scroll: win.scroll["asset_scenes"] = 0 if "asset_scene_selected" not in win.current: win.current["asset_scene_selected"] = False x = 10 y = 70 width = win.current["w"] / 4 - 20 height = win.current["h"] - 80 UI_elements.roundrect(layer, win, x, y, width, height, 10, fill=False) layer.clip() current_Y_scenes = 0 scenes = win.story["scenes"] for scene in scenes: fraction = scenes[scene]["fraction"] shots = scenes[scene]["shots"] foundinscene = [] for num, block in enumerate(shots): si = 1 if block[0] == "shot_block": si = 2 found = [] for n, stuff in enumerate(block[si]): # DIRECT LINK #print(block, si) if stuff[0] == "link" and stuff[1] == win.cur: start = "" part = stuff[-1] end = "" if n > 0: start = block[si][n-1][-1] start = start.replace("\n", " ") if n < len(block[si])-1: end = block[si][n+1][-1] end = end.replace("\n", " ") found = [start, part, end] if si == 2: foundinscene.append([block[1],found,num,n]) else: foundinscene.append(["",found,num,n]) # FRASE LINK if stuff[0] == "frase" and stuff[1][0] == "link" and stuff[1][1] == win.cur: start = "" part = stuff[1][-1] end = "" if n < len(block[si])-1: end = " : "+stuff[-1] found = [start, part, end] if si == 2: foundinscene.append([block[1],found,num ,n]) else: foundinscene.append(["",found,num ,n]) if foundinscene: UI_color.set(layer, win, "node_background") UI_elements.roundrect(layer, win, x, y+win.scroll["asset_scenes"]+current_Y_scenes, width, 70, 10) # ICON UI_elements.image(layer, win, "settings/themes/"+win.settings["Theme"]+"/icons/scene.png", 20, y+win.scroll["asset_scenes"] + current_Y_scenes+5, 40, 40) # SELECTION def do(): if win.current["asset_scene_selected"] == scene: win.current["asset_scene_selected"] = False else: win.current["asset_scene_selected"] = scene UI_elements.roundrect(layer, win, x, y+win.scroll["asset_scenes"]+current_Y_scenes, width, 70, 10, button=do, fill=False) layer.stroke() # SCENE NAME UI_color.set(layer, win, "text_normal") layer.set_font_size(20) layer.move_to( x+60, y+win.scroll["asset_scenes"] + current_Y_scenes+30) layer.show_text(scene) # FRACTION UI_color.set(layer, win, "progress_background") UI_elements.roundrect(layer, win, x+10, y+50+win.scroll["asset_scenes"] + current_Y_scenes, width-20, 0, 5) UI_color.set(layer, win, "progress_active") UI_elements.roundrect(layer, win, x+10, y+50+win.scroll["asset_scenes"] + current_Y_scenes, (width-20)*fraction, 0, 5) # IF SELECTED if win.current["asset_scene_selected"] == scene: UI_color.set(layer, win, "progress_background") UI_elements.roundrect(layer, win, x, y+win.scroll["asset_scenes"]+current_Y_scenes, width, 70, 10, fill=False) layer.stroke() # Here if the current scene is selected I want to draw all # the parts of the scene. It's already a bit more complex # then what was in the Blender-Organizer legacy. current_Y_scenes = current_Y_scenes + 80 for block in foundinscene: # If it's a shot let's mark in the different color. if block[0]: if "shot_colors" not in win.story: win.story["shot_colors"] = {} surl = "/"+scene+"/"+block[0] if surl not in win.story["shot_colors"]: rcolors = [ "shot_1", "shot_2", "shot_3", "shot_4", "shot_5" ] win.story["shot_colors"][surl] = rcolors[len(win.story["shot_colors"]) % len(rcolors)] col = win.story["shot_colors"][surl] UI_color.set(layer, win, col) else: UI_color.set(layer, win, "node_background") UI_elements.roundrect(layer, win, x, y+win.scroll["asset_scenes"]+current_Y_scenes, width, 30, 10) # GET TO THE SCENE BUTTON def do(): win.current["script_find"] = [block[2],block[3]] win.url = "script" print(scene) win.cur = "/"+scene+"/"+block[0] print(win.cur) UI_elements.roundrect(layer, win, x, y+win.scroll["asset_scenes"]+current_Y_scenes, width, 30, 10, button=do, fill=False) layer.stroke() start = block[1][0] name = block[1][1] end = block[1][2] sp = x+width/2-len(name)*12/2-len(start)*12 np = x+width/2-len(name)*12/2 ep = x+width/2+len(name)*12/2 if sp > x+5 and x+5 + ep + len(end)*12 > width: sp = x+5 np = x+5 + len(start)*12 ep = x+5 + len(start)*12 + len(name)*12 elif ep + len(end)*12 < width and sp < x+5: ep = width - len(end)*12 np = ep - len(name)*12 sp = np - len(start)*12 # BEFORE NAME UI_color.set(layer, win, "text_normal") layer.set_font_size(20) layer.move_to( sp, y+win.scroll["asset_scenes"] + current_Y_scenes+20) layer.show_text(start) # NAME AS MENTIONED IN THE SCRIPT UI_color.set(layer, win, "node_asset") UI_elements.roundrect(layer, win, np-6, y+win.scroll["asset_scenes"]+current_Y_scenes+2, len(name)*12+12, 26, 10) UI_color.set(layer, win, "text_normal") layer.set_font_size(20) layer.move_to(np, y+win.scroll["asset_scenes"] + current_Y_scenes+20) layer.show_text(name) # AFTER NAME UI_color.set(layer, win, "text_normal") layer.set_font_size(20) layer.move_to( ep, y+win.scroll["asset_scenes"] + current_Y_scenes+20) layer.show_text(end) current_Y_scenes = current_Y_scenes + 40 else: current_Y_scenes = current_Y_scenes + 80 # Scroll UI_elements.scroll_area(layer, win, "asset_scenes", x+0, y+50, width, height-50, current_Y_scenes, bar=True, mmb=True) return surface