diff --git a/studio/analytics.py b/studio/analytics.py index 917d528..6229446 100644 --- a/studio/analytics.py +++ b/studio/analytics.py @@ -3,12 +3,14 @@ import os import datetime +import json from studio import checklist from studio import story #import checklist #import story from settings import settings +from settings import talk def get_legacy(project_location): @@ -17,8 +19,10 @@ def get_legacy(project_location): # step of conversion. And used to display basic analitycs into the # project-manager. + name_tmp = project_location[project_location.rfind("/")+1:] + data = { - "name" : "", # Name of the project (typed properly) + "name" : name_tmp, # Name of the project (typed properly) "director" : "", # Name of the project's director. "status" : "", # Projects's comment / type "donework" : 0.0, # Percentage of Assets and Scenes done @@ -33,7 +37,7 @@ def get_legacy(project_location): "veh_factor" : 1, # Importance factor for Vehicles "loc_factor" : 1, # Importance factor for Locations "obj_factor" : 1, # Importance factor for Objects (Other) - "rnd_factor" : 1, # Importance factor for Scenes (Renders) + "rnd_factor" : 4, # Importance factor for Scenes (Renders) "chr" : 0.0, # Percentage of Characters done "veh" : 0.0, # Percentage of Vehicles done "loc" : 0.0, # Percentage of Locations done @@ -186,8 +190,11 @@ def get_legacy(project_location): # the percentage. Btw It's just a hard thing. That I needed a separate # function for it. - projectdata = checklist.get_list(project_location+"/project.progress") - data["checklist"] = projectdata["fraction"] + try: + projectdata = checklist.get_list(project_location+"/project.progress") + data["checklist"] = projectdata["fraction"] + except: + pass # NEXT THING. As I love to type it into place where people read me while I'm # working. We've got data from 2 files. Now we need to get data from ALL the @@ -603,124 +610,128 @@ def get_legacy(project_location): # print() # print() - #data_save(data) + #data_save(project_location, data) return data -def data_save(data): +def save(project, data): - # This function will save the data to the /set folder. It will include a - # a whole lot of inforamtion. Basically all analytics data will be there. + # This file will save analitycs data. - new_date_format = "%Y/%m/%d" + try: + os.mkdir(project+'/set/') + except: + pass - lines = [] - - lines.append("##################### VCStudio Analytics File #####################") - lines.append("") - lines.append(" Last update: "+datetime.datetime.strftime(datetime.datetime.today(), new_date_format)) - lines.append("") - lines.append(" Project : "+data["name"]) - lines.append(" Director: "+data["director"]) - lines.append(" Comment : "+data["status"]) - lines.append("") - lines.append(" From: "+data["startdate"]+" To: "+data["deadline"]) - lines.append("") - lines.append("-------------------------------------------------------------------") - lines.append("") - lines.append(" CURREND PROGRESS : "+str(data["fraction"]*100)+"%") - lines.append("") - lines.append(" Current actuall : "+str(data["donework"]*100)+"%") - lines.append(" Current checklist : "+str(data["checklist"]*100)+"%") - lines.append("") - lines.append(" Current Characters: "+str(data["chr"]*100)+"%") - lines.append(" Current Vehicled : "+str(data["veh"]*100)+"%") - lines.append(" Current Locations : "+str(data["loc"]*100)+"%") - lines.append(" Current Other(obj): "+str(data["obj"]*100)+"%") - lines.append(" Current Scenes : "+str(data["rnd"]*100)+"%") - lines.append("") - lines.append("-------------------------------------------------------------------") - lines.append("") - lines.append(" Bias:") - lines.append(" Characters: X "+str(data["chr_factor"])) - lines.append(" Vehicled : X "+str(data["veh_factor"])) - lines.append(" Locations : X "+str(data["loc_factor"])) - lines.append(" Other(obj): X "+str(data["obj_factor"])) - lines.append(" Scenes : X "+str(data["rnd_factor"])) - lines.append("") - lines.append("##################### Schedule / History #####################") - lines.append("") - - for date in sorted(data["dates"]): - lines.append(" date: "+date) + with open(project+'/set/analytics.json', 'w') as fp: + json.dump(data, fp, sort_keys=True, indent=4) - date = data["dates"][date] - - if "fractions" in date: - lines.append("") - lines.append(" Fraction : "+str(date["fractions"]["project"]*100)+"%") - lines.append("") - lines.append(" Characters: "+str(date["fractions"]["chr"]*100)+"%") - lines.append(" Vehicles : "+str(date["fractions"]["veh"]*100)+"%") - lines.append(" Locations : "+str(date["fractions"]["loc"]*100)+"%") - lines.append(" Other(obj): "+str(date["fractions"]["obj"]*100)+"%") - lines.append(" Scenes : "+str(date["fractions"]["rnd"]*100)+"%") - for thing in ["assets", "scenes", "files"]: - if thing in date: - - lines.append("") - lines.append(" type: "+thing+" :") - - for asset in sorted( date[thing] ): - lines.append(" link: "+asset+" : ") - lines.append("") - for task in sorted(date[thing][asset]): - - - if task[1] == "schedule": - lines.append(" at: "+task[0]) - lines.append(" action: "+task[1]) - lines.append("") - lines.append(" "+task[2]) - spaces = " " - for directory in task[3]: - spaces = spaces + ":..." - lines.append(spaces+directory) - - elif task[3] in ["[Un-Checked]", "[Checked]"]: - - lines.append(" at: "+task[0]) - lines.append(" action: "+task[1]+" "+task[3]) - lines.append("") - lines.append(" "+task[2]) - spaces = " " - for directory in task[4]: - spaces = spaces + ":..." - lines.append(spaces+directory) - - else: - lines.append(" at: "+task[0]) - lines.append(" action: "+task[1]+" "+task[3]) - lines.append(" "+task[2]) - - # Username - - lines.append("") - lines.append(" user: "+task[-1]) - - lines.append("") - +def load(project_location): + + # This is a simple load analytics funtion. + name_tmp = project_location[project_location.rfind("/")+1:] + + data = { + "name" : name_tmp, # Name of the project (typed properly) + "director" : "", # Name of the project's director. + "status" : "", # Projects's comment / type + "donework" : 0.0, # Percentage of Assets and Scenes done + "fraction" : 0.0, # Project's completion percentage + "checklist" : 0.0, # Project's main checklist percentage + "startdate" : "0000/00/00", # Date of the start of the project + "deadline" : "0000/00/00", # Date when project's deadline is + "duration" : 0, # Amount in days between startdate and deadline + "timepassed" : 0.0, # Percentage of how much time had passed + "dayspassed" : 0, # Amount of days since the startdate + "chr_factor" : 1, # Importance factor for Characters + "veh_factor" : 1, # Importance factor for Vehicles + "loc_factor" : 1, # Importance factor for Locations + "obj_factor" : 1, # Importance factor for Objects (Other) + "rnd_factor" : 4, # Importance factor for Scenes (Renders) + "chr" : 0.0, # Percentage of Characters done + "veh" : 0.0, # Percentage of Vehicles done + "loc" : 0.0, # Percentage of Locations done + "obj" : 0.0, # Percentage of Objects (Other) done + "rnd" : 0.0, # Percentage of Scenes (Renders) done + "dates" : {} # Per date, detailed data about the project + } + + try: + with open(project_location+'/set/analytics.json') as json_file: + data = json.load(json_file) + except: + pass + + try: + projectdata = checklist.get_list(project_location+"/set/project.progress") + data["checklist"] = projectdata["fraction"] + except: + make = open(project_location+"/set/project.progress", "w") + make.write("[ ] Story\n") + make.write("[ ] "+talk.text("chr")+"\n") + make.write("[ ] "+talk.text("veh")+"\n") + make.write("[ ] "+talk.text("loc")+"\n") + make.write("[ ] "+talk.text("obj")+"\n") + make.write("[ ] Animation\n") + make.write("[ ] Rendering") + make.close() + + # NEXT THING. As I love to type it into place where people read me while I'm + # working. We've got data from 2 files. Now we need to get data from ALL the + # project. + + # First we going to get data about the assets. Because it's relativelly easy + # compared to the story. For which you need to parce a crazy complicated .bos + # file. Which is a complex database in it's own right. + + # So let's go and quickly get data about the assets. + + asstfols = ["chr", "veh", "loc", "obj"] + astlist = [] + + for n , f in enumerate(asstfols): - lines.append("") - lines.append("-------------------------------------------------------------------") - lines.append("") - - #test = open("/home/vcs/Desktop/testfile.vcsa", "w") + flist = [] + + if len(os.listdir(project_location+"/dev/"+f)) > 0: + for asset in os.listdir(project_location+"/dev/"+f): + + if asset+".blend" in os.listdir(project_location+"/ast/"+f): + flist.append(1.0) + + else: + try: + fcheck = checklist.get_list(project_location+"/dev/"+f+"/"+asset+"/asset.progress") + flist.append(fcheck["fraction"]) + except: + flist.append(0.0) + + # The multiplication thing that I was talking about earlier. + + multiply = data[f+"_factor"] + for m in range(multiply): + astlist.append(sum(flist)/len(flist)) + + data[f] = sum(flist)/len(flist) - for i in lines: - print(i) - #test.write(i+"\n") + # For the next step I need to have the story parsed and read. But it's going + # to be so hard. That I will need to write a separate function for it. + + data["rnd"] = story.load(project_location)["fraction"] + + # After all of it we need to get the final project percentage. + multiply = data["rnd_factor"] + for m in range(multiply): + astlist.append(data["rnd"]) + + try: + data["donework"] = sum(astlist) / len(astlist) + except: + data["donework"] = 0.0 + data["fraction"] = (data["donework"] + data["checklist"]) / 2 + + + return data - #test.close() diff --git a/studio/checklist.py b/studio/checklist.py index dfdcd64..5216c0e 100644 --- a/studio/checklist.py +++ b/studio/checklist.py @@ -160,4 +160,33 @@ def get_list(filepath): return checklist +def get_fraction(win, path): + ############################################################################ + + # This function will return a fraction of a given checklist. It will ignore + # complitelly what is the asset / shot / project the checklist is from. + + # For sake of making this function actually somewhat useful, aka not bloated + # I will use it to cashe the whole checklist data objects. So they could be + # easily accesable later on. + + # This is usefull so I would not need to create 2 cash data structures. One + # for fractions and for the checklists them selves. + + ############################################################################ + + + if path not in win.checklists: + + # Let's check if path exists in the project first. + + if os.path.exists(win.project+"/"+path): + win.checklists[path] = get_list(win.project+"/"+path) + else: + win.checklists[path] = get_list(path) + + + # Let's now return back the fraction + + return win.checklists[path]["fraction"] diff --git a/studio/story.py b/studio/story.py index ffbf9f3..b4f7dae 100644 --- a/studio/story.py +++ b/studio/story.py @@ -3,6 +3,7 @@ import os import datetime +import json from studio import checklist #import checklist @@ -530,7 +531,7 @@ def get_legacy(project_location): link = link[:link.rfind("/renders/Preview.")].replace("/dev", "") data["links"].append([ - linktype, link, coordinates + linktype, link, coordinates, "" ]) # Markers. I nearly forgot about the markers. In the Story-Editor of @@ -555,7 +556,7 @@ def get_legacy(project_location): # which direction are they. For this markers will need to have # both X and Y coordinates. - data["markers"][markstring] = [markloc, 0.0] + data["markers"][markstring] = [markloc, 0.0, ""] # For now the Y is 0. Because we are reading from the old version. @@ -588,6 +589,9 @@ def get_legacy(project_location): except: data["fraction"] = 0.0 + #save(project_location, data) + #data = load(project_location) + return data def get_asset_data(win, name, force=False): @@ -613,3 +617,65 @@ def get_asset_data(win, name, force=False): win.assets[name] = data return win.assets[name] + +def save(project, story): + + # This is a save of the new VCStudio. Which is using a standard file format. + # so people could parse the data themselves. ( Or because I'm lazy ) + + try: + os.mkdir(project+'/pln/') + except: + pass + + with open(project+'/pln/story.vcss', 'w') as fp: + json.dump(story, fp, sort_keys=True, indent=4) + +def load(project): + + # This is a convertion back from JSON data to out beloved python dict + try: + with open(project+'/pln/story.vcss') as json_file: + data = json.load(json_file) + except: + + # If there is no file. + data = { + "fraction": 0.0, # Percentage of the Scenes finished. + "camera" : [0,0], # The position of where the user left + "selected": [], # List of selected items in the story editor + "active": None, # Active item. + "scenes" : {}, # List of scenes. + "arrows" : [], # List of connections. (A LIST. NOT DICT.) + "links" : [], # List of links to files or assets. + "markers": {}, # List of markers. + "events" : {} # List of frame like containers. (It will have similar) + } # function as events. But will have no text data with in + # it. It will be more like Blender's node editor's Frame. + + fractions = [] + lastarrow = 'start' + + + for i in data["arrows"]: + if lastarrow == "end": + break + for arrow in data["arrows"]: + if arrow[0] == lastarrow: + lastarrow = arrow[1] + if arrow[1] != "end": + fractions.append( + data["scenes"][arrow[1][1]]["fraction"] + ) + else: + break + + # FINAL STUFF... + + try: + data["fraction"] = sum(fractions) / len(fractions) + except: + data["fraction"] = 0.0 + + + return data diff --git a/studio/studio_asset_selectLayer.py b/studio/studio_asset_selectLayer.py index fa8c6b3..951be5f 100644 --- a/studio/studio_asset_selectLayer.py +++ b/studio/studio_asset_selectLayer.py @@ -25,6 +25,7 @@ from UI import UI_color # story from studio import story +from studio import analytics def layer(win, call): @@ -152,6 +153,10 @@ def layer(win, call): icon="cancel", tip=talk.text("cancel")) + # Short cut ESC + if 65307 in win.current["keys"] and not win.textactive: + do() + # Now let's prepare the ground for the next part. UI_elements.roundrect(layer, win, @@ -163,14 +168,7 @@ def layer(win, call): fill=False) layer.clip() - UI_color.set(layer, win, "dark_overdrop") - layer.rectangle( - 0, - 0, - win.current["w"], - win.current["h"], - ) - layer.fill() + tileX = 70 current_Y = 0 @@ -178,7 +176,12 @@ def layer(win, call): if "asset_select" not in win.scroll: win.scroll["asset_select"] = 0 + newcreate = win.text["asset_select_search"]["text"].replace("/","_").replace(" ", "_")\ + .replace('"',"_").replace("(","_").replace(")","_").replace("'","_")\ + .replace("[","_").replace("]","_").replace("{","_").replace("}","_") + ########################### + okay = True for asset in os.listdir(win.project+"/dev/"+win.current["asset_cur"]): if os.path.isdir(win.project+"/dev/"+win.current["asset_cur"]+"/"+asset): @@ -188,7 +191,7 @@ def layer(win, call): # Search if win.text["asset_select_search"]["text"]\ - and win.text["asset_select_search"]["text"].lower() not in asset.lower(): + and newcreate.lower() not in asset.lower(): okay = False if okay: @@ -196,13 +199,38 @@ def layer(win, call): if int(current_Y + win.scroll["asset_select"] + 100) in range(0-100, win.current["h"]): - UI_color.set(layer, win, "node_asset") - UI_elements.roundrect(layer, win, - tileX-10, - current_Y + win.scroll["asset_select"] + 120, + # 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) + 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") + node.rectangle(0,0,170, 20) + node.fill() + + # Outputting the layer + layer.set_source_surface(nodesurface, + tileX-10, + current_Y + win.scroll["asset_select"] + 120) + layer.paint() + # Previes image @@ -246,37 +274,43 @@ def layer(win, call): 0, 5) - UI_color.set(layer, win, "text_normal") - layer.set_font_size(12) - layer.move_to(tileX, - current_Y + win.scroll["asset_select"] + 135) - layer.show_text(asset[asset.rfind("/")+1:][:22]) - - - # Button to activate it - def do(): - win.current["calls"][call]["var"] = "/"+win.current["asset_cur"]+"/"+asset - win.assets = {} - - layer.set_line_width(4) - UI_elements.roundrect(layer, win, - tileX-10, - current_Y + win.scroll["asset_select"] + 120, - 170, - 200, - 10, - button=do, - tip=talk.text(win.current["asset_cur"])+": "+asset, - fill=False, - clip=[ - 50, - 100, - win.current["w"]-100, - win.current["h"]-200 - ]) - - layer.stroke() - layer.set_line_width(2) + UI_color.set(layer, win, "text_normal") + layer.set_font_size(12) + layer.move_to(tileX, + current_Y + win.scroll["asset_select"] + 135) + layer.show_text(asset[asset.rfind("/")+1:][:22]) + + + + + + + + + # Button to activate it + def do(): + win.current["calls"][call]["var"] = "/"+win.current["asset_cur"]+"/"+asset + win.assets = {} + + layer.set_line_width(4) + UI_elements.roundrect(layer, win, + tileX-10, + current_Y + win.scroll["asset_select"] + 120, + 170, + 200, + 10, + button=do, + tip=talk.text(win.current["asset_cur"])+": "+asset, + fill=False, + clip=[ + 50, + 100, + win.current["w"]-100, + win.current["h"]-200 + ]) + + layer.stroke() + layer.set_line_width(2) tileX += 200 @@ -285,6 +319,71 @@ def layer(win, call): current_Y += 230 + + + if win.text["asset_select_search"]["text"]\ + and newcreate\ + not in os.listdir(win.project+"/dev/"+win.current["asset_cur"]): + + # If there is a seach and there is no asset with the search name. It + # will ask if you want to create such an asset. + + def do(): + + # I don't thing I need to write a whole module for creating a couple + # of folders. + + try: + os.mkdir(win.project+"/dev/"+win.current["asset_cur"]+"/"+newcreate) + os.mkdir(win.project+"/dev/"+win.current["asset_cur"]+"/"+newcreate+"/renders") + os.mkdir(win.project+"/dev/"+win.current["asset_cur"]+"/"+newcreate+"/reference") + os.mkdir(win.project+"/dev/"+win.current["asset_cur"]+"/"+newcreate+"/tex") + except: + pass + + win.text["asset_select_search"]["text"] = "" + + win.current["calls"][call]["var"] = "/"+win.current["asset_cur"]+"/"+newcreate + win.assets = {} + + + # Refrashing analytics + win.analytics = analytics.load(win.project) + analytics.save(win.project, win.analytics) + + UI_elements.roundrect(layer, win, + tileX-10, + current_Y + win.scroll["asset_select"] + 120, + 170, + 200, + 10, + button=do, + tip=talk.text("create_new_asset")+" "+newcreate) + + UI_color.set(layer, win, "progress_background") + UI_elements.roundrect(layer, win, + tileX-10, + current_Y + win.scroll["asset_select"] + 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_select"] + 200, + 40, 40) + + UI_color.set(layer, win, "text_normal") + layer.set_font_size(12) + layer.move_to(tileX+85-len(newcreate)*4, + current_Y + win.scroll["asset_select"] + 300) + layer.show_text(newcreate) + + + ########################### current_Y += 230 diff --git a/studio/studio_file_selectLayer.py b/studio/studio_file_selectLayer.py index 0610f01..bfdcbca 100644 --- a/studio/studio_file_selectLayer.py +++ b/studio/studio_file_selectLayer.py @@ -23,6 +23,8 @@ from project_manager import pm_project from UI import UI_elements from UI import UI_color +# Studio +from studio import checklist def layer(win, call): @@ -80,7 +82,7 @@ def layer(win, call): if "AllFiles" not in win.current: win.current["AllFiles"] = [] - for r, d, f in os.walk(win.project): + for r, d, f in sorted(os.walk(win.project)): for item in f: win.current["AllFiles"].append(os.path.join(r, item).replace(win.project, "")) @@ -153,8 +155,32 @@ def layer(win, call): ##### BOTTOM BUTTONS #### + def do(): + filechooser = Gtk.FileChooserDialog(talk.text("select_file"), + None, + Gtk.FileChooserAction.OPEN, + (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, + Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) + filechooser.set_default_response(Gtk.ResponseType.OK) + response = filechooser.run() + if response == Gtk.ResponseType.OK: + get = filechooser.get_filename() + + win.current["calls"][call]["var"] = get + + del win.current["AllFiles"] + + filechooser.destroy() - + UI_elements.roundrect(layer, win, + win.current["w"]-120, + win.current["h"]-80, + 40, + 40, + 10, + button=do, + icon="folder", + tip=talk.text("outside_folder")) def do(): win.current["calls"][call]["var"] = False @@ -171,6 +197,10 @@ def layer(win, call): icon="cancel", tip=talk.text("cancel")) + # Short cut ESC + if 65307 in win.current["keys"] and not win.textactive: + do() + # Now let's prepare the ground for the next part. UI_elements.roundrect(layer, win, @@ -182,14 +212,7 @@ def layer(win, call): fill=False) layer.clip() - UI_color.set(layer, win, "dark_overdrop") - layer.rectangle( - 0, - 0, - win.current["w"], - win.current["h"], - ) - layer.fill() + ### ACTUALL FILES LIST ### @@ -255,7 +278,7 @@ def layer(win, call): for f in fileformats.images: if filename.endswith(f): typefound = True - UI_color.set(layer, win, "node_imagefile") + thecoloris = "node_imagefile" if not win.current["file_selector"]["image"]: okay = False break @@ -265,7 +288,7 @@ def layer(win, call): for f in fileformats.videos: if filename.endswith(f): typefound = True - UI_color.set(layer, win, "node_videofile") + thecoloris = "node_videofile" if not win.current["file_selector"]["video"]: okay = False break @@ -274,16 +297,16 @@ def layer(win, call): # Blend Files if filename.endswith(".blend"): typefound = True - UI_color.set(layer, win, "node_blendfile") + thecoloris = "node_blendfile" if "ast/" in filename: - UI_color.set(layer, win, "node_asset") + thecoloris = "node_asset" if not win.current["file_selector"]["blender"]: okay = False if okay: if typefound == False: - UI_color.set(layer, win, "node_script") + thecoloris = "node_script" if not win.current["file_selector"]["file"]: okay = False @@ -296,19 +319,67 @@ def layer(win, call): if int(current_Y + win.scroll["file_select"] + 100) in range(0-100, win.current["h"]): - UI_elements.roundrect(layer, win, - tileX-10, - current_Y + win.scroll["file_select"] + 120, + # 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) + 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, thecoloris) + node.rectangle(0,0,170, 20) + node.fill() + + # Outputting the layer + layer.set_source_surface(nodesurface, + tileX-10, + current_Y + win.scroll["file_select"] + 120) + layer.paint() UI_elements.image(layer, win, win.project+filename, tileX, current_Y + win.scroll["file_select"] + 150, 150, 150) - + + # If this is a checklist + + if filename.endswith(".progress"): + + fraction = checklist.get_fraction(win, filename) + + UI_color.set(layer, win, "progress_background") + UI_elements.roundrect(layer, win, + tileX, + current_Y + win.scroll["file_select"] + 300, + 150, + 0, + 5) + + UI_color.set(layer, win, "progress_active") + UI_elements.roundrect(layer, win, + tileX, + current_Y + win.scroll["file_select"] + 300, + 150*fraction, + 0, + 5) + + UI_color.set(layer, win, "text_normal") layer.set_font_size(12) layer.move_to(tileX, diff --git a/studio/studio_gtk.py b/studio/studio_gtk.py index 7544ece..4d8504f 100644 --- a/studio/studio_gtk.py +++ b/studio/studio_gtk.py @@ -36,15 +36,13 @@ def previous(win): win.previous[i] = win.current[i] # OK let's make a window -def run(win): +def run(project, win): # In the Blender-Organizer I was putting the version into the title. Not cool. # Because if you would snap it to the sidebar in Ubuntu. On mouse over it would # show the first ever version. So there will be a better way to see version. # I think let's do that like in Blender. Drawn with in the window somewhere. - project = win.current["project"] win.destroy() - # Setting up the window win = Gtk.Window() win.maximize() @@ -88,13 +86,14 @@ def run(win): } win.calllayer = False win.layercashe = {} # Here I gonna store layers that are inactive to speed up stuff + win.checklists = {} if pm_project.is_legacy(project): win.story = story.get_legacy(project) win.analytics = analytics.get_legacy(project) else: - win.story = {} - win.analytics = {} + win.story = story.load(project) + win.analytics = analytics.load(project) # Try to put a name of the project at the title win.set_title("VCStudio : "+win.analytics["name"]) diff --git a/studio/studio_nodes.py b/studio/studio_nodes.py index f24de79..4691dac 100644 --- a/studio/studio_nodes.py +++ b/studio/studio_nodes.py @@ -18,7 +18,9 @@ from settings import oscalls # Studio from studio import story +from studio import analytics from studio import studio_dialogs +from studio import checklist # UI modules from UI import UI_testing @@ -91,6 +93,10 @@ def node_dot(layer, win, x, y, direction="in", entry="end"): except: pass + # Refrashing analytics + story.save(win.project, win.story) + win.analytics = analytics.load(win.project) + analytics.save(win.project, win.analytics) # Connecting the line @@ -110,10 +116,13 @@ def node_dot(layer, win, x, y, direction="in", entry="end"): win.story["arrows"].append(new_arrow) - - UI_math.filter_arrows(win) + # Refrashing analytics + story.save(win.project, win.story) + win.analytics = analytics.load(win.project) + analytics.save(win.project, win.analytics) + if direction != "in": win.out_dots[entry] = [x+6, y+6] @@ -280,7 +289,7 @@ def scene_node(outlayer, win, x, y, width, height, name="Unknown", fraction=0.0) width = max(width,20) height = max(height,20) - + doparent = True if int(x) in range(int(0-width), int(win.current["w"]))\ and int(y) in range(int(0-height), int(win.current["h"])): @@ -292,6 +301,7 @@ def scene_node(outlayer, win, x, y, width, height, name="Unknown", fraction=0.0) selected = False + if win.url == "story_editor"\ and int(win.current["mx"]) in range(50, int(win.current["w"]-50)) \ and int(win.current["my"]) in range(50, int(win.current["h"]-30)): @@ -410,6 +420,11 @@ def scene_node(outlayer, win, x, y, width, height, name="Unknown", fraction=0.0) # Now let's do the simple grab tool. if win.current["tool"] == "grab": + + # If you press shift, You take things out of events + if 65505 in win.current["keys"]: + doparent = False + try: @@ -426,9 +441,25 @@ def scene_node(outlayer, win, x, y, width, height, name="Unknown", fraction=0.0) x += win.previous["mx"] - win.previous["LMB"][0] y += win.previous["my"] - win.previous["LMB"][1] + + # Parent removing / assigning - - + if not doparent: + win.story["scenes"][name]["parent"] = "" + + elif not win.story["scenes"][name]["parent"]: + + for event in win.story["events"]: + + mx, my = win.story["events"][event]["position"] + pmx, pmy = win.story["events"][event]["size"] + + if UI_math.rectangle_overlap( + [mx, my, pmx, pmy], + [x,y,width, height]): + + win.story["scenes"][name]["parent"] = event + break elif win.current["tool"] != "connect": @@ -470,6 +501,12 @@ def scene_node(outlayer, win, x, y, width, height, name="Unknown", fraction=0.0) fill=False) layer.clip() + # Tooltip + + if int(win.current["mx"]) in range(int(x), int(x+width))\ + and int(win.current["my"]) in range(int(y), int(y+height)): + UI_elements.tooltip(win, name) + # Background UI_color.set(layer, win, "dark_overdrop") layer.rectangle(0,0,width, height) @@ -512,7 +549,7 @@ def scene_node(outlayer, win, x, y, width, height, name="Unknown", fraction=0.0) # In case there is a parent event in the scene. - if win.story["scenes"][name]["parent"]: + if win.story["scenes"][name]["parent"] and doparent: parent = win.story["scenes"][name]["parent"] UI_math.rectangle_surround(win, parent, win.story["events"][parent]["position"], @@ -592,7 +629,9 @@ def link_node(outlayer, win, x, y, width=150, height=150, name="", num=0, linkty for f in fileformats.videos: if name.endswith(f): filetype = "video" - + + doparent = True + if int(x) in range(int(0-width), int(win.current["w"]))\ and int(y) in range(int(0-height), int(win.current["h"])): @@ -722,6 +761,11 @@ def link_node(outlayer, win, x, y, width=150, height=150, name="", num=0, linkty if win.current["tool"] == "grab": + + # If you press shift, You take things out of events + if 65505 in win.current["keys"]: + doparent = False + try: if win.current["LMB"]: x += win.current["mx"] - win.current["LMB"][0] @@ -736,6 +780,25 @@ def link_node(outlayer, win, x, y, width=150, height=150, name="", num=0, linkty x += win.previous["mx"] - win.previous["LMB"][0] y += win.previous["my"] - win.previous["LMB"][1] + + # Parent removing / assigning + + if not doparent: + win.story["links"][num][3] = "" + + elif not win.story["links"][num][3]: + + for event in win.story["events"]: + + mx, my = win.story["events"][event]["position"] + pmx, pmy = win.story["events"][event]["size"] + + if UI_math.rectangle_overlap( + [mx, my, pmx, pmy], + [x,y,width, height]): + + win.story["links"][num][3] = event + break elif win.current["tool"] != "connect": win.current["tool"] = "selection" @@ -929,6 +992,26 @@ def link_node(outlayer, win, x, y, width=150, height=150, name="", num=0, linkty name, 0, 0, width, height) + if name.endswith(".progress"): + + fraction = checklist.get_fraction(win, name) + + UI_color.set(layer, win, "progress_background") + UI_elements.roundrect(layer, win, + 10, + height-20, + width-20, + 0, + 5) + + UI_color.set(layer, win, "progress_active") + UI_elements.roundrect(layer, win, + 10, + height-20, + (width-20)*fraction, + 0, + 5) + if name.endswith(".blend"): UI_color.set(layer, win, "node_blendfile") if "ast/" in name: @@ -941,7 +1024,14 @@ def link_node(outlayer, win, x, y, width=150, height=150, name="", num=0, linkty else: UI_color.set(layer, win, "node_script") + + + # Tooltip + if int(win.current["mx"]) in range(int(x), int(x+width))\ + and int(win.current["my"]) in range(int(y), int(y+height)): + UI_elements.tooltip(win, name) + # top banner @@ -978,12 +1068,24 @@ def link_node(outlayer, win, x, y, width=150, height=150, name="", num=0, linkty del win.out_dots["file:"+name] except: pass + + # In case there is a parent event in the scene. + + if win.story["links"][num][3] and doparent: + parent = win.story["links"][num][3] + UI_math.rectangle_surround(win, parent, + win.story["events"][parent]["position"], + win.story["events"][parent]["size"], + [x,y], [width, height] + ) def marker(outlayer, win, name ,x, y ): # This function will draw markers to the screen. They are like shortcuts in # a story editor space. + + if int(x) in range(int(60), int(win.current["w"]-100))\ and int(y) in range(int(60), int(win.current["h"]-80)): width = 200 @@ -1008,6 +1110,8 @@ def marker(outlayer, win, name ,x, y ): if inscreen: + doparent = True + selected = False if win.url == "story_editor"\ and int(win.current["mx"]) in range(50, int(win.current["w"]-50)) \ @@ -1110,6 +1214,11 @@ def marker(outlayer, win, name ,x, y ): if win.current["tool"] == "grab": + + # If you press shift, You take things out of events + if 65505 in win.current["keys"]: + doparent = False + try: if win.current["LMB"]: x += win.current["mx"] - win.current["LMB"][0] @@ -1125,6 +1234,28 @@ def marker(outlayer, win, name ,x, y ): x += win.previous["mx"] - win.previous["LMB"][0] y += win.previous["my"] - win.previous["LMB"][1] + + # Parent removing / assigning + + if not doparent: + win.story["markers"][name][2] = "" + + elif not win.story["markers"][name][2] : + + for event in win.story["events"]: + + mx, my = win.story["events"][event]["position"] + pmx, pmy = win.story["events"][event]["size"] + + if UI_math.rectangle_overlap( + [mx, my, pmx, pmy], + [x,y,width, height]): + + win.story["markers"][name][2] = event + print(event) + break + + elif win.current["tool"] != "connect": win.current["tool"] = "selection" @@ -1191,10 +1322,13 @@ def marker(outlayer, win, name ,x, y ): if name != win.text[name+"_marker"]["text"] and win.text[name+"_marker"]["text"]: def do(): + + win.story["selected"] = [] win.story["markers"][win.text[name+"_marker"]["text"]] = [ x - win.story["camera"][0], - y - win.story["camera"][1] + y - win.story["camera"][1], + win.story["markers"][name][2] ] win.textactive = "" @@ -1213,7 +1347,17 @@ def marker(outlayer, win, name ,x, y ): if 65293 in win.current["keys"]: do() - + + # In case there is a parent event in the scene. + + if win.story["markers"][name][2] and doparent: + parent = win.story["markers"][name][2] + UI_math.rectangle_surround(win, parent, + win.story["events"][parent]["position"], + win.story["events"][parent]["size"], + [x,y], [width, height] + ) + else: if win.textactive == name+"_marker": @@ -1238,4 +1382,4 @@ def marker(outlayer, win, name ,x, y ): fill=False) outlayer.stroke() - + diff --git a/studio/studio_storyLayer.py b/studio/studio_storyLayer.py index 4ad3cbc..eed72a5 100644 --- a/studio/studio_storyLayer.py +++ b/studio/studio_storyLayer.py @@ -19,6 +19,8 @@ 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 #UI modules from UI import UI_elements @@ -210,7 +212,8 @@ def layer(win): "asset", var, [ win.current["mx"]-win.story["camera"][0]-75, win.current["my"]-win.story["camera"][1]-75 - ] + ], + "" # Parent ]) # Now let's select and move the thing @@ -246,7 +249,8 @@ def layer(win): "file", var, [ win.current["mx"]-win.story["camera"][0]-75, win.current["my"]-win.story["camera"][1]-75 - ] + ], + "" # Parent ]) # Now let's select and move the thing @@ -275,22 +279,6 @@ def layer(win): do() win.current["keys"] = [] - - # Event - def do(): - print("Event") - - UI_elements.roundrect(layer, win, - 5, - 255, - 40, - 40, - 10, - do, - "event", - talk.text("event_tooltip"), - url="story_editor") - # Marker def do(): @@ -303,7 +291,8 @@ def layer(win): win.story["markers"][markername] = [ win.current["mx"]-win.story["camera"][0]+50, - win.current["my"]-win.story["camera"][1]-20 + win.current["my"]-win.story["camera"][1]-20, + "" # Parent ] win.textactive = markername+"_marker" @@ -319,14 +308,62 @@ def layer(win): UI_elements.roundrect(layer, win, 5, - 305, + 255, 40, 40, 10, do, "pin", - talk.text("marker_tooltip"), + talk.text("marker_tooltip")+"\n[M]", url="story_editor") + + # Shortcut + if 109 in win.current["keys"] and not win.textactive: + do() + win.current["keys"] = [] + + + if win.story["selected"]: + # Event + def do(): + eventname = "Event" + count = 2 + while eventname in win.story["events"]: + eventname = "Event_"+str(count) + count = count + 1 + + win.story["events"][eventname] = { + "position":[0,0], + "size":[0,0] + } + + # Even going to delete it self if there will be noone who parenting + # it. + + for thing in win.story["selected"]: + if thing[0] == "scene": + win.story["scenes"][thing[1]]["parent"] = eventname + elif thing[0] in ["file", "asset"]: + win.story["links"][thing[1]][3] = eventname + elif thing[0] == "marker": + win.story["markers"][thing[1]][2] = eventname + + UI_elements.roundrect(layer, win, + 5, + 305, + 40, + 40, + 10, + do, + "event", + talk.text("event_tooltip")+"\n[E]", + url="story_editor") + + # Shortcut + if 101 in win.current["keys"] and not win.textactive: + do() + win.current["keys"] = [] + # Renders def do(): @@ -548,20 +585,51 @@ def layer(win): # EVENTS (Frames) - - for event in win.story["events"]: - - # Loaction - sx, sy = win.story["events"][event]["position"] - - - # Scale - ssx, ssy = win.story["events"][event]["size"] - - - #Draw - studio_nodes.event_node(layer, win, sx, sy, ssx, ssy, name=event) - + try: + for event in win.story["events"]: + + # Loaction + sx, sy = win.story["events"][event]["position"] + + + # Scale + ssx, ssy = win.story["events"][event]["size"] + + + #Draw + studio_nodes.event_node(layer, win, sx, sy, ssx, ssy, name=event) + + # Let's now check if the event even has anybody inside. It's a bit + # not the best way to implement it yet. Because I will need to look + # through all items. But we can make it simpler if we find that it has + # we can just break out of a thing. + + found = False + + for scene in win.story["scenes"]: + if event == win.story["scenes"][scene]["parent"]: + found = True + break + + if not found: + for link in win.story["links"]: + if event == link[3]: + found = True + break + + if not found: + for marker in win.story["markers"]: + if event == win.story["markers"][marker][2]: + found = True + break + + + # If nobody is inside. Delete the bastard. + + if not found: + del win.story["events"][event] + except: + pass # SCENES @@ -722,8 +790,11 @@ def layer(win): # Undo selection if int(win.current["LMB"][0] - win.current["mx"]) in range(-10, 10)\ and int(win.current["LMB"][1] - win.current["my"])in range(-10, 10)\ + and int(win.current["mx"]) in range(50, int(win.current["w"]-50)) \ + and int(win.current["my"]) in range(50, int(win.current["h"]-30))\ and 65505 not in win.current["keys"]: - win.story["selected"] = [] + win.story["selected"] = [] + win.textactive = "" @@ -748,7 +819,12 @@ def layer(win): layer.stroke() except: pass - + + # Save story. I'm going to do it the same way as in the old Blender-Organizer + if win.current["frame"] % 10 == 0 and win.url == "story_editor"\ + and not win.current["LMB"] and not win.current["MMB"] and not win.current["RMB"]: + story.save(win.project, win.story) + analytics.save(win.project, win.analytics) # To selected if 65454 in win.current["keys"] and win.story["selected"] and not win.textactive: