diff --git a/studio/analytics.py b/studio/analytics.py index f29734d..a1ff28f 100644 --- a/studio/analytics.py +++ b/studio/analytics.py @@ -7,13 +7,42 @@ import json from studio import checklist from studio import story -#import checklist -#import story +from studio import schedule + from settings import settings from settings import talk +def iftime(string): + + if len(string) != len("00:00:00"): + return False + + if len(string.split(":")) != 3: + return False + + try: + for n, i in enumerate(string.split(":")): + + if len(i) != 2: + return False + + i = int(i) + + if n == 0 and i > 23: + return False + if i > 59: + return False + except: + pass + + + return True def ifdate(string): + + if len(string) != len("1997/07/30"): + return False + new_date_format = "%Y/%m/%d" try: datetime.datetime.strptime(string, new_date_format) @@ -717,6 +746,12 @@ def load(project_location): if data["timepassed"] > 1.0: data["timepassed"] = 1.0 + # LET's FILTER THE SCHEDULES + + data["dates"] = schedule.filter(project_location, data["dates"]) + + + # 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. @@ -773,5 +808,20 @@ def load(project_location): data["fraction"] = (data["donework"] + data["checklist"]) / 2 + # Let's record it for today. + today = datetime.datetime.strftime(datetime.datetime.today(), new_date_format) + if today not in data["dates"]: + data["dates"][today] = {} + data["dates"][today]["fractions"] = { + "project":data["fraction"], + "checklist":data["checklist"], + "chr":data["chr"], + "veh":data["veh"], + "loc":data["loc"], + "obj":data["obj"], + "rnd":data["rnd"] + } + + return data diff --git a/studio/checklist.py b/studio/checklist.py index c496a02..121d9e1 100644 --- a/studio/checklist.py +++ b/studio/checklist.py @@ -11,6 +11,9 @@ from UI import UI_color #settings from settings import talk +#studio +from studio import analytics + def get_list(filepath): # This fucntion converts text documents. (.progress) into a more machine @@ -201,6 +204,34 @@ def get_fraction(win, path): return win.checklists[path]["fraction"] +def get_task_by_path(tasks, path, p=[]): + + ############################################################################ + + # This function will give the information about a given task from a non + # recursive URL such as ["Task", "Sub-task", "Sub-task2"]. This will look + # the recursive list and output the given task. In this case "Sub-task2" + # with all the information inside it. + + ############################################################################ + + + for task in tasks: + + pa = p.copy() + pa.append(" "+task["string"]) + + + if pa == path: + return task + + if task["subtasks"]: + t = get_task_by_path(task["subtasks"], path, pa) + if t: + return t + + return False + def filter_tasks(data): ############################################################################ @@ -272,7 +303,7 @@ def save(path, data): w.close() -def draw(outlayer, win, path): +def draw(outlayer, win, path, back="story_editor"): ############################################################################ @@ -391,13 +422,66 @@ def draw(outlayer, win, path): if "moving_task_now" not in win.current: win.current["moving_task_now"] = False - def draw_tasks(tasks, cXY): + def draw_tasks(tasks, cXY, schedulep): + + + + ######################################### + # # + # THIS IS THE RECURSIVE FUNCTION # + # # + ######################################### + + # I will try to explain what is going on here. But it will require me + # understanding the code myself. And it's a bit redic. There was a hell + # of a lot of copy-paste, edit value, continue. for num , task in enumerate(tasks): - # Delete + # This is the code that will be done for each task in the list. Or + # subtask if a task have them. - if 65535 in win.current["keys"] and task["selected"]: + # Let's get a schedule path. A folder like structure. + # Exacmple: + + # Instead of: + # Task + # Sub-Task + # Sub-Task 2 + + # We get: + # ["Task", "Sub-Task", "Sub-Task 2"] + + schedulepath = schedulep.copy() + schedulepath.append(" "+task["string"]) + + + ###### SCHEDULING STUFF BACKWARD ###### + + # This is a set of stuff to make schedules work with checklists. + + if "schedule_task_selected" not in win.current: + win.current["schedule_task_selected"] = False + + if win.current["schedule_task_selected"]: + csl = win.current["schedule_task_selected"][0][0][4] + + if " "+task["string"] in csl and not task["open"]: + task["open"] = True + + if schedulepath == csl and not task["selected"]: + filter_tasks(win.checklists[path]) + task["selected"] = True + win.scroll["checklist"] = 0 - cXY[1] + height/2 + + + #### DELETE SHORT KEY #### + + # There is probably a reason to put it here. Probably something + # breaks if you put it anywhere else. IDK actually. Test it. I + # don't remember + + if 65535 in win.current["keys"] and task["selected"] and not win.current["schedule_task_selected"]: del tasks[num] win.current["keys"] = [] @@ -405,53 +489,139 @@ def draw(outlayer, win, path): save(path, win.checklists[path]) win.checklists = {} win.assets = {} + win.analytics = analytics.load(win.project) + + #### THE GRABBING FUNCTION #### + + # This is the activation of the grab tool. It uses the same + # win.current["tool"] = "grab" as the story editor. Because it makes + # sense to reuse it if it already exists. For sake of conviniense. + + # It doesn't mean that it uses the same code. It's a bit different. + # due to the nature of lists vs free positioning items. + + # Now let's set up a few positional variables. So I could move the tasks + # while moving the currently selected task. And in the same time will not + # screw the data. + + # So I copy the X and the Y locations like this. + + sx = cXY[0] + sy = win.scroll["checklist"] + cXY[1] + + grabY = 40 + if task["subtasks"]: + grabY = 60 # Grab if win.current["LMB"]\ - and int(win.current["LMB"][0]) in range(int(x+cXY[0]), int(x+cXY[0]+width))\ - and int(win.current["LMB"][1]) in range(int(y+cXY[1]), int(y+cXY[1]+height))\ - and win.current["tool"] == "selection" and task["selected"]\ - and int(win.current["LMB"][0]) not in range(int(win.current["mx"]-1), int(win.current["mx"]+1))\ - and int(win.current["LMB"][1]) not in range(int(win.current["my"]-1), int(win.current["my"]+1)): + and int(win.current["LMB"][0]) in range(int(x+sx), int(x+sx+width))\ + and int(win.current["LMB"][1]) in range(int(y+sy), int(y+sy+grabY))\ + and win.current["tool"] == "selection"\ + and int(win.current["LMB"][0]) not in range(int(win.current["mx"]-2), int(win.current["mx"]+2))\ + and int(win.current["LMB"][1]) not in range(int(win.current["my"]-2), int(win.current["my"]+2)): + + # So here we set up the grab tool and creating a little variable. + # This variable (moving_task_now) will consist of 2 parts on release. + + # 1. The entire task ( will subtasks ) using pop. Which is not simply + # a copy. But also removing the task from the checklist's list. + + # 2. The current frame of poping. So to offset insertion for 1 frame. + # This insures that you insert the task in the correct spot. + + filter_tasks(win.checklists[path]) + task["selected"] = True + win.current["schedule_task_selected"] = False win.current["tool"] = "grab" win.current["moving_task_now"] = False + # Now let's actually setup the pop. So when the mouse is now pressed, but + # was on a previous framae, and our current tool is grab. + if not win.current["LMB"] and win.previous["LMB"] and win.current["tool"] == "grab"\ and task["selected"]: - win.current["moving_task_now"] = tasks.pop(num) + # We remove the task from the list. And write it to the variable. With + # the current frame. + win.current["moving_task_now"] = [tasks.pop(num), win.current["frame"]] + + - sx = cXY[0] - sy = win.scroll["checklist"] + cXY[1] + # Now I can touch the sx and sy without screwing up the cXY or scroll. - thismoving = False - somemoving = False - someXY = [0,0] + thismoving = False # This is going to be True is this is the task that's + # moving currently + somemoving = False # This is going to be True if any task is moving + someXY = [0,0] # And this is the mouse position translated to location + # of the checklist. (x, y coordinates of the whole + # widget). Or in other words position of the task. + + # Then comes some logic to determen those 3 values. if win.current["tool"] == "grab": + # Okay so here. If grab first we assume that it's some other + # task. And now currently selected one. + somemoving = True someXY = [ win.current["mx"]-x, win.current["my"]-y ] + # Now this is the editing of the sx and sy values. To be at + # the position of the mouse. if task["selected"]: - sx = win.current["mx"] - x + 5 - sy = win.current["my"] - y + 5 + + # Calculating so the mouse will end up about in the centre + # of the task while the task is at motion. + + sx = win.current["mx"] - x - (width - cXY[0])/2 + sy = win.current["my"] - y - 10 task["open"] = False thismoving = True + somemoving = False + + # Now if the mouse is outside the frame of the checklist + # it will activate the scheduling. + + ############################################################ + # SCHEDULING PART # + ############################################################ + + if win.current["mx"] < x: + win.url = "analytics" + win.current["grab_data"] = [path, back, win.cur, schedulepath] + win.current["tool"] = "schedule" + # + # + # + # SEE studio/studio_analyticsLayer.py + # for more details about scheduling + # + + # And if back into frame it will return to normal grab mode. + + elif win.current["tool"] == "schedule" and win.current["mx"] > x: + win.url = back + win.current["tool"] = "grab" + + #################################################################### + # SCHEDULING PART END # + #################################################################### + inside = False between = False if sy-10 < someXY[1] < sy+10 and somemoving and not task["selected"]: - if win.current["moving_task_now"]: - tasks.insert(num, win.current["moving_task_now"]) + if win.current["moving_task_now"] and win.current["moving_task_now"][1] != win.current["frame"]: + tasks.insert(num, win.current["moving_task_now"][0]) win.current["tool"] = "selection" win.current["LMB"] = False win.previous["LMB"] = False @@ -460,17 +630,18 @@ def draw(outlayer, win, path): save(path, win.checklists[path]) win.checklists = {} win.assets = {} + win.analytics = analytics.load(win.project) elif win.current["LMB"]: - UI_color.set(layer, win, "progress_background") - UI_elements.roundrect(layer, win, - cXY[0], - win.scroll["checklist"] + cXY[1], - width - cXY[0], - 40, - 10, - fill=False) - layer.stroke() + + for line in range(int(cXY[0]/20)): + + line = line * 20 + 10 + + UI_color.set(layer, win, "node_background") + layer.move_to(line, win.scroll["checklist"] + cXY[1]-10) + layer.line_to(line, win.scroll["checklist"] + cXY[1]+40) + layer.stroke() cXY[1] = cXY[1] + 50 @@ -482,8 +653,8 @@ def draw(outlayer, win, path): elif sy < someXY[1] < sy + 40 and somemoving and not task["selected"]: - if win.current["moving_task_now"]: - task["subtasks"].append(win.current["moving_task_now"]) + if win.current["moving_task_now"] and win.current["moving_task_now"][1] != win.current["frame"]: + task["subtasks"].append(win.current["moving_task_now"][0]) win.current["tool"] = "selection" win.current["LMB"] = False win.previous["LMB"] = False @@ -492,6 +663,7 @@ def draw(outlayer, win, path): save(path, win.checklists[path]) win.checklists = {} win.assets = {} + win.analytics = analytics.load(win.project) elif win.current["LMB"]: inside = True @@ -501,6 +673,7 @@ def draw(outlayer, win, path): def do(): ed = task["selected"] and not task["editing"] filter_tasks(win.checklists[path]) + win.current["schedule_task_selected"] = False task["selected"] = True if ed: task["editing"] = True @@ -508,7 +681,7 @@ def draw(outlayer, win, path): sx+40, sy, width - cXY[0]-40, - 40, + grabY, 10, button=do, offset=[x,y], @@ -577,6 +750,7 @@ def draw(outlayer, win, path): save(path, win.checklists[path]) win.checklists = {} win.assets = {} + win.analytics = analytics.load(win.project) UI_elements.roundrect(layer, win, sx, @@ -661,16 +835,16 @@ def draw(outlayer, win, path): def do(): task["open"] = not task["open"] + win.current["schedule_task_selected"] = False # Saving save(path, win.checklists[path]) - win.checklists = {} - win.assets = {} - + + UI_elements.roundrect(layer, win, sx, sy, 40, - 40, + 60, 10, button=do, icon=im, @@ -695,7 +869,7 @@ def draw(outlayer, win, path): save(path, win.checklists[path]) win.checklists = {} win.assets = {} - + win.analytics = analytics.load(win.project) if not task["editing"]: layer.set_font_size(20) @@ -788,13 +962,13 @@ def draw(outlayer, win, path): # THERE IS YOUR RECURSION if task["open"]: - draw_tasks(task["subtasks"], cXY) + draw_tasks(task["subtasks"], cXY, schedulepath) cXY[0] = cXY[0] - 20 # Adding subtasks - if ((task["subtasks"] and task["open"])\ + if ((task["subtasks"] and task["open"] and task["selected"])\ or (task["selected"] and not task["subtasks"]))\ and win.textactive != "editing_task"\ and win.current["tool"] == "selection": @@ -856,16 +1030,16 @@ def draw(outlayer, win, path): cXY[0] = cXY[0] - 20 cXY[1] = cXY[1] + 50 - - draw_tasks(win.checklists[path]["subtasks"], cXY) + schedulepath = [] + draw_tasks(win.checklists[path]["subtasks"], cXY, schedulepath) # Go to the bottom. if win.current["tool"] == "grab"\ - and win.current["my"] - y > cXY[1]: + and win.current["my"] - y > win.scroll["checklist"] + cXY[1]: - if win.current["moving_task_now"]: - win.checklists[path]["subtasks"].append(win.current["moving_task_now"]) + if win.current["moving_task_now"] and win.current["moving_task_now"][1] != win.current["frame"]: + win.checklists[path]["subtasks"].append(win.current["moving_task_now"][0]) win.current["tool"] = "selection" win.current["LMB"] = False win.previous["LMB"] = False @@ -874,17 +1048,8 @@ def draw(outlayer, win, path): save(path, win.checklists[path]) win.checklists = {} win.assets = {} - - elif win.current["LMB"]: - UI_color.set(layer, win, "progress_background") - UI_elements.roundrect(layer, win, - cXY[0], - win.scroll["checklist"] + cXY[1], - width - cXY[0], - 40, - 10, - fill=False) - layer.stroke() + win.analytics = analytics.load(win.project) + @@ -935,6 +1100,13 @@ def draw(outlayer, win, path): tileX, current_Y = cXY + # So there would not be jumps of stuff. Let's add heigh to the scroll while + # in grab. + if win.current["tool"] == "grab": + current_Y = current_Y + height + + + # Outputting the layer outlayer.set_source_surface(surface, x, y) outlayer.paint() diff --git a/studio/schedule.py b/studio/schedule.py new file mode 100644 index 0000000..3b73cea --- /dev/null +++ b/studio/schedule.py @@ -0,0 +1,610 @@ +# 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 handle SCHEDULING. Primarily the output of the schedules +# to the screen. But also probably a lot of other things related to schediles. + +################################################################################ + +def filter(project, dates): + + ############################################################################ + + # This function is called on reading of analytics read. It will check whether + # schedules are finished. Or if they are even found. Using studio/checklist.py + # as a help. + + ############################################################################ + + checkcashe = {} # Temporary checklist cashe. + + delete = [] # Stuff I want to delete later + + for date in dates: + for i in ["assets", "scenes", "files"]: + if i in dates[date]: + for url in dates[date][i]: + for num, entry in enumerate(dates[date][i][url]): + if dates[date][i][url][num][1] == "schedule": + + # First of all. I didn't think about it when + # writting the analytics read + # that I will need to know whether schedules + # are [Checked] or [Un-Checked] + + if not "[Checked]" in dates[date][i][url][num]\ + and not "[Un-Checked]" in dates[date][i][url][num]: + dates[date][i][url][num].insert(3, "[Un-Checked]") + + # Then let's actually try to find whether the task + # is checked. + + + # For now let's do in a simple way. Because + # if the whole checklist is 100% done. Then + # obviously the task is also checked. + + path = dates[date][i][url][num][2] + + + try: + + # Let's check if path exists in the project first. + if os.path.exists(project+"/dev"+url+path): + path = project+"/dev"+url+path + + + elif os.path.exists(project+"rnd/"+url+path): + path = project+"/rnd"+url+path + + elif os.path.exists(project+"/set/"+path): + path = project+"/set/"+path + + elif os.path.exists(project+"/"+path): + path = project+"/"+path + + if path not in checkcashe: + checkcashe[path] = checklist.get_list(path) + + # Here we check whether the checklist is 100% + + if checkcashe[path]["fraction"] == 1.0: + dates[date][i][url][num][3] = "[Checked]" + + # But what if it's not? Now we need to check + # them 1 by one. + + else: + + dates[date][i][url][num][3] = "[Un-Checked]" + + task = checklist.get_task_by_path(checkcashe[path]["subtasks"],dates[date][i][url][num][4]) + + # Now if the task is not found. We delete the schedule + if task: + if task["fraction"] == 1.0: + dates[date][i][url][num][3] = "[Checked]" + + else: + delete.append([date,i,url,dates[date][i][url][num]]) + except: + delete.append([date,i,url,dates[date][i][url][num]]) + + # Deleting all kinds of buddah + + for i in delete: + q,w,e,r = i + if r in dates[q][w][e]: + dates[q][w][e].remove(r) + + + + return dates + +def get_schedules(dates): + + ############################################################################ + + # This function will parse the dates data from the analytics. And get only + # the schedules. In the format {date {url, [list of schedules]}} + + ############################################################################ + newdates = {} + + for date in dates: + if date not in newdates: + newdates[date] = {} + + for i in ["assets", "scenes", "files"]: + if i in dates[date]: + for url in dates[date][i]: + for num, entry in enumerate(dates[date][i][url]): + if entry[1] == "schedule": + if url not in newdates[date]: + newdates[date][url] = [] + if [entry, num] not in newdates[date][url]: + newdates[date][url].append([entry, num]) + + + return newdates + + +def draw(outlayer, win): + + x = 10 + y = 70 + width = win.current["w"] / 4 - 20 + height = win.current["h"] - 80 + + # At the top above the schedules. In the bar. i want to put 2 + # buttons. One will hide done schedules, the other will + # hide everything scheduled for other users. + + if "schedule_layer_settings" not in win.current: + win.current["schedule_layer_settings"] = { + "checked":False, + "multiuser":False + } + + for num, button in enumerate(win.current["schedule_layer_settings"]): + + if win.current["schedule_layer_settings"][button]: + + UI_color.set(outlayer, win, "progress_time") + UI_elements.roundrect(outlayer, win, + 20+width-(40*num)-60, + 15, + 40, + 40, + 10) + + def do(): + win.current["schedule_layer_settings"][button] = not win.current["schedule_layer_settings"][button] + + UI_elements.roundrect(outlayer, win, + 20+width-(40*num)-60, + 15, + 40, + 40, + 10, + do, + button) + + + # 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() + + + # Getting various screadule data + schedules = get_schedules(win.analytics["dates"]) + new_date_format = "%Y/%m/%d" + today = datetime.datetime.strftime(datetime.datetime.today(), new_date_format) + + slist = [] + + for date in schedules: + + if win.current["date"] != today and date != win.current["date"]: + continue + + for item in schedules[date]: + if win.cur == item or not win.cur: + + slist.append([date, item, schedules[date][item]]) + + + # Selection magic + if "schedule_task_selected" not in win.current: + win.current["schedule_task_selected"] = False + + # Let's draw them to the screen in some way + + if "schedule" not in win.scroll: + win.scroll["schedule"] = 0 + + current_Y = 0 + + for entry in slist: + + for thing in entry[2]: + + # Parsing the cur to get name and type + name = entry[1][entry[1].rfind("/")+1:] + acur = entry[1].replace(name, "").replace("/", "") + fullurl = "" + for e in thing[0][4][:-1]: + fullurl = fullurl+e+" > " + + UI_color.set(layer, win, "node_background") + + # If not all users show only current user's tasks + if not win.current["schedule_layer_settings"]["multiuser"]\ + and thing[0][-1] != win.settings["Username"]: + continue + + # If the task is checked + if thing[0][3] == "[Checked]": + if not win.current["schedule_layer_settings"]["checked"]: + continue + UI_color.set(layer, win, "node_blendfile") # The Green + + + elif entry[0] != "1997/07/30": + + if entry[0] < today: + UI_color.set(layer, win, "node_badfile") # The Red + elif entry[0] > today: + UI_color.set(layer, win, "node_asset") # The Purple + + UI_elements.roundrect(layer, win, + 0, + win.scroll["schedule"] + current_Y, + width, + 75, + 10) + + # Selection button + def do(): + if win.current["schedule_task_selected"] != [ thing, entry[1] ]: + win.current["schedule_task_selected"] = [ thing, entry[1] ] + + else: + win.current["schedule_task_selected"] = False + + # Clearing the text + try: + del win.text["schedule_username_setting"] + del win.text["schedule_date_setting"] + del win.text["schedule_time_setting"] + except: + pass + UI_elements.roundrect(layer, win, + 0, + win.scroll["schedule"] + current_Y, + width, + 75, + 10, + button=do, + tip=entry[1]+" : "+fullurl+thing[0][4][-1], + offset=[x,y], + fill=False) + layer.stroke() + + # ICON + UI_elements.image(layer, win, + "settings/themes/"+win.settings["Theme"]+"/icons/schedule.png", + 5, win.scroll["schedule"] + 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["schedule"] + current_Y+25, + ) + layer.show_text(thing[0][4][-1]) + + # TASK URL INSIDE THE CHECKLIST + + if fullurl: + layer.set_font_size(10) + layer.move_to( + 60+len(thing[0][4][-1])*12, + win.scroll["schedule"] + current_Y+25, + ) + layer.show_text(fullurl) + + # NAME OF THE ASSET / SHOT + if acur in ["chr", "veh", "loc", "obj"]: + assetname = talk.text(acur)+": "+name + else: + assetname = entry[1] + + layer.set_font_size(15) + layer.move_to( + 50, + win.scroll["schedule"] + current_Y+45, + ) + layer.show_text(assetname) + + # DATE + if entry[0] != "1997/07/30" and entry[0] != today: + layer.set_font_size(15) + layer.move_to( + width-130, + win.scroll["schedule"] + current_Y+45, + ) + layer.show_text(entry[0]) + + # TIME + + layer.set_font_size(15) + layer.move_to( + width-130, + win.scroll["schedule"] + current_Y+65, + ) + layer.show_text(thing[0][0]) + + # USERNAME + layer.set_font_size(15) + layer.move_to( + 20, + win.scroll["schedule"] + current_Y+65, + ) + layer.show_text(talk.text("user_schedules")+" "+thing[0][-1]) + + # IF SELECTED THERE WILL BE MORE STUFF + if win.current["schedule_task_selected"] == [ thing, entry[1] ]: + UI_color.set(layer, win, "text_normal") + UI_elements.roundrect(layer, win, + 1, + win.scroll["schedule"] + current_Y, + width-2, + 75, + 10, + fill=False) + layer.stroke() + + current_Y = current_Y + 85 + + # If it's a task from an asset or a scene. There should be a link + # to it. But first let's check that's it's infect an asset. + # because... + + if entry[1]: + + # If asset: + if acur in ["chr", "veh", "loc","obj"]: + # ICON + UI_elements.image(layer, win, + "settings/themes/"+win.settings["Theme"]+"/icons/"+acur+".png", + 25, win.scroll["schedule"] + current_Y+5, 40, 40) + + goto = "assets" + itemtype = "assets" + + elif not acur: + itemtype = "files" + else: + # ICON + UI_elements.image(layer, win, + "settings/themes/"+win.settings["Theme"]+"/icons/vse.png", + 25, win.scroll["schedule"] + current_Y+5, 40, 40) + + goto = "script" + itemtype = "scenes" + + + # Here comes the link button + + def do(): + win.url = goto + win.cur = entry[1] + win.current["asset_left_panel"] = "schedule" + UI_elements.roundrect(layer, win, + 20, + win.scroll["schedule"] + current_Y+5, + width-20, + 40, + 10, + button=do, + tip=entry[1]+" : "+fullurl+thing[0][4][-1], + offset=[x,y], + fill=False) + layer.stroke() + + #Title + UI_color.set(layer, win, "text_normal") + layer.set_font_size(20) + layer.move_to( + 80, + win.scroll["schedule"] + current_Y+30, + ) + layer.show_text(assetname) + + current_Y = current_Y + 50 + + # Next thing! Let's show the most editable values. I think the first + # one. For the director will be important. Is to edit the USER. + # or in other words. Who is doing the task. + + # In the end It will be a drop down menu. Like all of it. But for this + # I need to implement MULTIUSER first. And it's not a priority right + # now. So a simple text editor could be okay. + + # ICON + UI_elements.image(layer, win, + "settings/themes/"+win.settings["Theme"]+"/icons/user.png", + 25, win.scroll["schedule"] + current_Y+5, 40, 40) + + UI_elements.text(layer, win, "schedule_username_setting", + 80, + win.scroll["schedule"] + current_Y+5, + width-80, + 40, + set_text=thing[0][-1], + tip=talk.text("username"), + offset=[x,y]) + + if win.text["schedule_username_setting"]["text"] != thing[0][-1]: + def do(): + thing[0][-1] = win.text["schedule_username_setting"]["text"] + analytics.save(win.project, win.analytics) + + + UI_elements.roundrect(layer, win, + width-40, + win.scroll["schedule"] + current_Y+5, + 40, + 40, + 10, + button=do, + icon="ok", + tip=talk.text("checked"), + offset=[x,y]) + + current_Y = current_Y + 50 + + # DATE : TIME + + UI_elements.image(layer, win, + "settings/themes/"+win.settings["Theme"]+"/icons/schedule.png", + 25, win.scroll["schedule"] + current_Y+5, 40, 40) + + UI_elements.text(layer, win, "schedule_date_setting", + 80, + win.scroll["schedule"] + current_Y+5, + (width-80)/2-20, + 40, + set_text=entry[0], + tip=talk.text("username"), + offset=[x,y]) + + ### DELETE KEY ### + + if acur in ["chr", "veh", "loc","obj"]: + itemtype = "assets" + elif not acur: + itemtype = "files" + else: + itemtype = "scenes" + + if 65535 in win.current["keys"]: + pop = win.analytics["dates"][entry[0]][itemtype]\ + [entry[1]].pop(thing[1]) + win.current["keys"] = [] + + analytics.save(win.project, win.analytics) + win.analytics = analytics.load(win.project) + + if win.text["schedule_date_setting"]["text"] != entry[0]\ + and analytics.ifdate(win.text["schedule_date_setting"]["text"]): + def do(): + + + + pop = win.analytics["dates"][entry[0]][itemtype]\ + [entry[1]].pop(thing[1]) + + if win.text["schedule_date_setting"]["text"] not in win.analytics["dates"]: + win.analytics["dates"][win.text["schedule_date_setting"]["text"]] = {} + if itemtype not in win.analytics["dates"][win.text["schedule_date_setting"]["text"]]: + win.analytics["dates"][win.text["schedule_date_setting"]["text"]][itemtype] = {} + if entry[1] not in win.analytics["dates"][win.text["schedule_date_setting"]["text"]][itemtype]: + win.analytics["dates"][win.text["schedule_date_setting"]["text"]][itemtype][entry[1]] = [] + + win.analytics["dates"][win.text["schedule_date_setting"]["text"]][itemtype]\ + [entry[1]].append(pop) + + analytics.save(win.project, win.analytics) + win.analytics = analytics.load(win.project) + + UI_elements.roundrect(layer, win, + (width-80)/2+20, + win.scroll["schedule"] + current_Y+5, + 40, + 40, + 10, + button=do, + icon="ok", + tip=talk.text("checked"), + offset=[x,y]) + + # TIME + + UI_elements.text(layer, win, "schedule_time_setting", + 80+(width-80)/2, + win.scroll["schedule"] + current_Y+5, + (width-80)/2, + 40, + set_text=thing[0][0], + tip=talk.text("username"), + offset=[x,y]) + + if win.text["schedule_time_setting"]["text"] != thing[0][0]\ + and analytics.iftime(win.text["schedule_time_setting"]["text"]): + def do(): + thing[0][0] = win.text["schedule_time_setting"]["text"] + + analytics.save(win.project, win.analytics) + win.analytics = analytics.load(win.project) + + + UI_elements.roundrect(layer, win, + width-40, + win.scroll["schedule"] + current_Y+5, + 40, + 40, + 10, + button=do, + icon="ok", + tip=talk.text("checked"), + offset=[x,y]) + + + + current_Y = current_Y + 70 + + + else: + current_Y = current_Y + 85 + + + + # Outputting the layer + outlayer.set_source_surface(surface, x, y) + outlayer.paint() + + # Scroll + UI_elements.scroll_area(outlayer, win, "schedule", + x+0, + y+50, + width, + height-50, + current_Y, + bar=True, + mmb=True) diff --git a/studio/studio_analyticsLayer.py b/studio/studio_analyticsLayer.py new file mode 100644 index 0000000..af04c17 --- /dev/null +++ b/studio/studio_analyticsLayer.py @@ -0,0 +1,909 @@ +# THIS FILE IS A PART OF VCStudio +# PYTHON 3 + +# This a console project manager. + +import os +import datetime + +# 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 + +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, + 10, + win.current["w"]/2, + win.current["h"]-20, + 10) + + ############################################################################ + + # This is the Analitycs window. This time I want to do something crazy. + # There will be 5 sections on the screen. + + ############################################################################ + # # # # + # # # # + # # PROGRESS BARS # # + # # # # + # # # # + # # ####################################### # # + # # # # + # SCHEDULING # ANALYTICS GRAPH # MAIN # + # # # CHECKLIS # + # # # # + # # ####################################### # # + # # # # + # # CALENDAR / DAY SELECTOR # # + # # # # + # # X # # + ############################################################################ + + # The idea is that you could be in Assets or Script Editor and drag the + # tasks from the checklist down to Calendar and leave it there. So when you + # move the task and mouse comes into the center part, you automatically + # transferred to the analytics layer for untill you leave the task somewhere + # or return it back to the checklist. + + # I want to do the same with the SCHEDULING. So you could simple drag it to + # the Calendar to change it's date. Let's see how terribly hard will it be + # to implement. Because I'm already fighting with the Scedules all day. + + ############################################################################ + + ############## PROGRESS BARS ############# + + + timepassed = 0.0 + projectdone = 0.0 + chrdone = 0.0 + vehdone = 0.0 + locdone = 0.0 + objdone = 0.0 + rnddone = 0.0 + + try: + timepassed = win.analytics["timepassed"] + projectdone = win.analytics["fraction"] + chrdone = win.analytics["chr"] + vehdone = win.analytics["veh"] + locdone = win.analytics["loc"] + objdone = win.analytics["obj"] + rnddone = win.analytics["rnd"] + except: + pass + + # MAIN PROGRESS + + # Icon + UI_elements.image(layer, win, "settings/themes/"\ + +win.settings["Theme"]+"/icons/analytics.png", + win.current["w"]/4+10, + 15, + 40, + 40) + + # Progressbar + UI_color.set(layer, win, "progress_background") + UI_elements.roundrect(layer, win, + win.current["w"]/4+60, + 25, + win.current["w"]/2-80, + 20, + 10) + + + + # Project Done + UI_color.set(layer, win, "progress_active") + UI_elements.roundrect(layer, win, + win.current["w"]/4+60, + 25, + (win.current["w"]/2-80)*projectdone, + 20, + 10) + + # TIME PASSED + + # Icon + UI_elements.image(layer, win, "settings/themes/"\ + +win.settings["Theme"]+"/icons/schedule.png", + win.current["w"]/4+10, + 55, + 40, + 40) + + UI_color.set(layer, win, "progress_background") + UI_elements.roundrect(layer, win, + win.current["w"]/4+60, + 65, + win.current["w"]/2-80, + 20, + 10) + + # Timepassed + UI_color.set(layer, win, "progress_time") + UI_elements.roundrect(layer, win, + win.current["w"]/4+60, + 65, + (win.current["w"]/2-80)*timepassed, + 20, + 10) + + # SCENES DONE ( RND ) + + # Icon + UI_elements.image(layer, win, "settings/themes/"\ + +win.settings["Theme"]+"/icons/vse.png", + win.current["w"]/4+10, + 95, + 40, + 40) + + UI_color.set(layer, win, "progress_background") + UI_elements.roundrect(layer, win, + win.current["w"]/4+60, + 105, + win.current["w"]/2-80, + 20, + 10) + + # Scenes + UI_color.set(layer, win, "node_videofile") + UI_elements.roundrect(layer, win, + win.current["w"]/4+60, + 105, + (win.current["w"]/2-80)*rnddone, + 20, + 10) + + # CHR DONE + + # Icon + UI_elements.image(layer, win, "settings/themes/"\ + +win.settings["Theme"]+"/icons/chr.png", + win.current["w"]/4+10, + 135, + 40, + 40) + + UI_color.set(layer, win, "progress_background") + UI_elements.roundrect(layer, win, + win.current["w"]/4+60, + 145, + win.current["w"]/4-80, + 20, + 10) + + # progress + UI_color.set(layer, win, "node_asset") + UI_elements.roundrect(layer, win, + win.current["w"]/4+60, + 145, + (win.current["w"]/4-80)*chrdone, + 20, + 10) + + # VEH DONE + + # Icon + UI_elements.image(layer, win, "settings/themes/"\ + +win.settings["Theme"]+"/icons/veh.png", + win.current["w"]/2, + 135, + 40, + 40) + + UI_color.set(layer, win, "progress_background") + UI_elements.roundrect(layer, win, + win.current["w"]/2+60, + 145, + win.current["w"]/4-80, + 20, + 10) + + # progress + UI_color.set(layer, win, "node_imagefile") + UI_elements.roundrect(layer, win, + win.current["w"]/2+60, + 145, + (win.current["w"]/4-80)*vehdone, + 20, + 10) + + # LOC DONE + + # Icon + UI_elements.image(layer, win, "settings/themes/"\ + +win.settings["Theme"]+"/icons/loc.png", + win.current["w"]/4+10, + 175, + 40, + 40) + + UI_color.set(layer, win, "progress_background") + UI_elements.roundrect(layer, win, + win.current["w"]/4+60, + 185, + win.current["w"]/4-80, + 20, + 10) + + # progress + UI_color.set(layer, win, "node_blendfile") + UI_elements.roundrect(layer, win, + win.current["w"]/4+60, + 185, + (win.current["w"]/4-80)*locdone, + 20, + 10) + + # OBJ DONE + + # Icon + UI_elements.image(layer, win, "settings/themes/"\ + +win.settings["Theme"]+"/icons/obj.png", + win.current["w"]/2, + 175, + 40, + 40) + + UI_color.set(layer, win, "progress_background") + UI_elements.roundrect(layer, win, + win.current["w"]/2+60, + 185, + win.current["w"]/4-80, + 20, + 10) + + # progress + UI_color.set(layer, win, "node_badfile") + UI_elements.roundrect(layer, win, + win.current["w"]/2+60, + 185, + (win.current["w"]/4-80)*objdone, + 20, + 10) + + ############### THE GRAPH ################## + + # This graph going to show the entire time of the whole project from + # StartDate till Deadline. It will fill up with data overtime. + + # We are going to have a couple of modes. + + # Regular mode: Showing actuall values represented on the graph + # it's going to be a diagonal line if the work was done + # linearly. + # Normalized : This this a graph of true values compared to time. So let's + # say at a second day you had to be only at 3%. Being at 3% will + # make graph show 100%. The expected value is linear from 0% + # to 100% over the whole project. + # Pulse mode : This mode will give a graph compared to the previous day. + # if on a given day there was a gib jump in percentage there + # will be a big spike on the graph. + + # Let's make a mode selector. + + if "analytics_middle_graph_mode" not in win.current: + win.current["analytics_middle_graph_mode"] = "pulse" + + for num, thing in enumerate(["linear", "analytics", "pulse"]): # By icons + + if win.current["analytics_middle_graph_mode"] == thing: + + UI_color.set(layer, win, "progress_time") + UI_elements.roundrect(layer, win, + win.current["w"]/4+10+(40*num), + 225, + 40, + 40, + 10) + + def do(): + win.current["analytics_middle_graph_mode"] = thing + del win.current["graph_cashe"] + + UI_elements.roundrect(layer, win, + win.current["w"]/4+10+(40*num), + 225, + 40, + 40, + 10, + do, + thing) + + # And before we start a little settings icon. + + # Settings + def do(): + win.url = "settings_layer" + + UI_elements.roundrect(layer, win, + win.current["w"]/4*3-60, + 225, + 40, + 40, + 10, + do, + "settings", + talk.text("Settings")) + + # Now let's make a filter by type. As you maybe already know appart from + # the main progress I'm reading all the other things too. Every progress + # bar on the screen should have a corrisponding graph. But sometimes if + # paths go too far appart, this aint helpfull. So a filter is required to + # switch a category on and off. + + if "analytics_middle_graph_switch" not in win.current: + win.current["analytics_middle_graph_switch"] = { + "project":[True,"analytics", "progress_active"], + "checklist":[True,"checklist", "darker_parts"], + "rnd":[True,"vse", "node_videofile"], + "chr":[True,"chr", "node_asset"], + "veh":[True,"veh", "node_imagefile"], # Name in data : [ Active, Icon name, color ] + "loc":[True,"loc", "node_blendfile"], + "obj":[True,"obj", "node_badfile"] + } + + cat = win.current["analytics_middle_graph_switch"] + mode = win.current["analytics_middle_graph_mode"] + + for num, thing in enumerate(cat): + + if cat[thing][0]: + + UI_color.set(layer, win, cat[thing][2]) + UI_elements.roundrect(layer, win, + win.current["w"]/4+160+(40*num), + 225, + 40, + 40, + 10) + + def do(): + cat[thing][0] = not cat[thing][0] + del win.current["graph_cashe"] + + UI_elements.roundrect(layer, win, + win.current["w"]/4+160+(40*num), + 225, + 40, + 40, + 10, + do, + cat[thing][1]) + + # Let's set up some very handy values + new_date_format = "%Y/%m/%d" + startdate = win.analytics["startdate"] + deadline = win.analytics["deadline"] + duration = win.analytics["duration"] + + # Let's setup the little graph layer. So nothing come out of the frame. + + x = win.current["w"]/4 + y = 280 + width = win.current["w"] / 2 + height = (win.current["h"]-300)/2 + + # Now let's make a layer. + + if "graph_cashe" not in win.current: + + + # Making the layer + graphsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height)) + node = cairo.Context(graphsurface) + node.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) + + + # Background + #UI_color.set(node, win, "dark_overdrop") + #node.rectangle(0,0,width, height) + #node.fill() + + # helping line + UI_color.set(node, win, "progress_background") + + if mode == "analytics": + + node.move_to(0, height/2) + node.line_to(width, height/2) + + elif mode == "pulse": + + ty = height / duration + node.move_to(0, height/2-ty) + node.line_to(width, height/2-ty) + + else: + + node.move_to(0, height) + node.line_to(width, 0) + + node.stroke() + + + for num, thing in enumerate(reversed(cat)): + + if cat[thing][0]: + + UI_color.set(node, win, cat[thing][2]) + + if mode == "linear": + node.move_to(0, height) + else: + node.move_to(0, height/2) + + pfrac = 0 + + dates = win.analytics["dates"] + for date in dates: + + # Let's calculate the X position of a given part on a graph + + sd = datetime.datetime.strptime(startdate, new_date_format) + nd = datetime.datetime.strptime(date , new_date_format) + + dn = nd - sd + dn = int(dn.days) + + graphX = width / duration * dn + + # Let's calculate the Y position of a given part on a graph + + if "fractions" in dates[date]: + fracs = dates[date]["fractions"] + + if mode == "linear": + gfraction = fracs[thing] + graphY = height - height * gfraction + node.line_to(graphX, graphY) + elif mode == "analytics": + gfraction = fracs[thing] + tfraction = dn / duration + gfraction = gfraction / tfraction / 2 + graphY = height - height * gfraction + node.line_to(graphX, graphY) + else: + gfraction = fracs[thing] + gfraction = gfraction - pfrac + graphY = height - height * gfraction - height / 2 + node.line_to(graphX, graphY) + + pfrac = fracs[thing] + + node.stroke() + + win.current["graph_cashe"] = graphsurface + # Outputting the layer + layer.set_source_surface(win.current["graph_cashe"], x, y) + layer.paint() + + + ############### SCHEDULING / DATE SELECTION ################ + + + # So here I want to put a dialog that in the Blender-Organizer legacy was + # in the center of the frame. I thought here it makes a bit more sense. Tho + # I will remove the graph from it. I have a very good graph on the top from + # it. And in my opinion this is enough. In you think otherwise let me know. + # or fork VCStudio and implement it yourself. See what I can do with free- + # software. I can just tell you to do everything yourself. Imaging doing this + # when developing proprietery software. + + # Anyway back to the thing. I do want to draw schedules inside the days cells. + # and by their colors. RED or GREY or PURPLE or GREEN you will have an idea of + # how much stuff is done and how much stuff is not yet done. A kind of second + # graph, so to speak. But representing differnt kind of data. + + + # OKAY. SETTINGS. + + if "schedule_analytics_settings" not in win.current: + win.current["schedule_analytics_settings"] = { + "checked":False, + "multiuser":False + } + + for num, button in enumerate(win.current["schedule_analytics_settings"]): + + if win.current["schedule_analytics_settings"][button]: + + UI_color.set(layer, win, "progress_time") + UI_elements.roundrect(layer, win, + x+10+(40*num), + y+height+10, + 40, + 40, + 10) + + def do(): + win.current["schedule_analytics_settings"][button] = not win.current["schedule_analytics_settings"][button] + + UI_elements.roundrect(layer, win, + x+10+(40*num), + y+height+10, + 40, + 40, + 10, + do, + button) + + # CURRENT DATE + today = datetime.datetime.strftime(datetime.datetime.today(), new_date_format) + + UI_elements.text(layer, win, "current_date_setting", + x+100, + y+height+10, + 200, + 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"] + + UI_elements.roundrect(layer, win, + x+260, + y+height+10, + 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 + + UI_elements.roundrect(layer, win, + x+260, + y+height+10, + 40, + 40, + 10, + button=do, + icon="cancel", + tip=talk.text("cancel")) + + + y = y + height + 60 + height = height - 100 + + # Making the layer + graphsurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height)) + node = cairo.Context(graphsurface) + node.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) + + + + + if "days" not in win.scroll: + win.scroll["days"] = 0 - win.analytics["dayspassed"]*50 + width / 2 - 25 + + current_X = 0 + + prevyear = [startdate.split("/")[0], win.scroll["days"]] + prevmonth = [startdate.split("/")[1], win.scroll["days"]] + + + for doffset in range(duration+1): # FOR ALL DAYS. NO MATTER IF THEY ARE IN DATA + + sd = datetime.datetime.strptime(startdate, new_date_format) + theday = datetime.datetime.strftime(sd+datetime.timedelta(days=doffset), new_date_format) + + + nowyear = [theday.split("/")[0], current_X+win.scroll["days"]] + nowmonth = [theday.split("/")[1], current_X+win.scroll["days"]] + + # Focusing if selected + + if win.current["date"] != win.previous["date"] and win.current["date"] == theday: + win.scroll["days"] = 0 - current_X + width / 2 - 25 + + # YEARS + + if nowyear[0] != prevyear[0]: + UI_color.set(node, win, "dark_overdrop") + UI_elements.roundrect(node, win, + 5+prevyear[1]+2, + 0, + nowyear[1] - prevyear[1]-7, + 20, + 10) + + + UI_color.set(node, win, "text_normal") + node.set_font_size(15) + node.move_to( + max(prevyear[1] + 200, min(width/2-23, nowyear[1]- 200)), + 15, + ) + node.show_text(prevyear[0]) + + prevyear = nowyear + + # MONTHS + + if nowmonth[0] != prevmonth[0]: + UI_color.set(node, win, "dark_overdrop") + UI_elements.roundrect(node, win, + 5+prevmonth[1]+2, + 22, + nowmonth[1]-prevmonth[1]-7, + 20, + 10) + + UI_color.set(node, win, "text_normal") + node.set_font_size(15) + node.move_to( + max(prevmonth[1] + 12, min(width/2-12, nowmonth[1]- 35)), + 38, + ) + node.show_text(prevmonth[0]) + + prevmonth = nowmonth + + # DAYS + if -50 < current_X+win.scroll["days"] < width: + + UI_color.set(node, win, "dark_overdrop") + UI_elements.roundrect(node, win, + 5+current_X+win.scroll["days"], + 44, + 40, + 20, + 10) + + UI_color.set(node, win, "text_normal") + node.set_font_size(15) + node.move_to( + 15+current_X+win.scroll["days"], + 59, + ) + node.show_text(theday.split("/")[2]) + + UI_color.set(node, win, "dark_overdrop") + if theday == today: + UI_color.set(node, win, "button_clicked") + UI_elements.roundrect(node, win, + 5+current_X+win.scroll["days"], + 67, + 40, + height-67, + 10) + + if win.current["date"] == theday: + UI_color.set(node, win, "progress_background") + UI_elements.roundrect(node, win, + 5+current_X+win.scroll["days"], + 67, + 40, + height-67, + 10, + fill=False) + node.stroke() + + # SELECTION BUTTON + + def do(): + win.current["date"] = theday + win.text["current_date_setting"]["text"] = theday + UI_elements.roundrect(node, win, + 5+current_X+win.scroll["days"], + 67, + 40, + height-67, + 10, + button=do, + offset=[x,y], + fill=False) + node.stroke() + + + # Now here I want to draw the representations of scheduled + # tasks that are insife + + sch = [] + if theday in win.analytics["dates"]: + date = win.analytics["dates"][theday] + for i in ["files", "assets", "scenes"]: + if i in date: + for item in date[i]: + for stuff in date[i][item]: + if stuff[1] == "schedule": + if not win.current["schedule_analytics_settings"]["multiuser"]: + if win.settings["Username"] != stuff[-1]: + continue + if "[Checked]" in stuff: + if win.current["schedule_analytics_settings"]["checked"]: + sch.append(True) + else: + sch.append(False) + for n, s in enumerate(sch): + UI_color.set(node, win, "node_background") + if theday < today: + UI_color.set(node, win, "node_badfile") + elif theday > today: + UI_color.set(node, win, "node_asset") + if s: + UI_color.set(node, win, "node_blendfile") + + UI_elements.roundrect(node, win, + 8+current_X+win.scroll["days"], + 70+13*n, + 35, + 8, + 5) + + + current_X = current_X + 50 + + + UI_color.set(node, win, "dark_overdrop") + UI_elements.roundrect(node, win, + 5+prevyear[1]+2, + 0, + nowyear[1] - prevyear[1]-4 + 50, + 20, + 10) + + + UI_color.set(node, win, "text_normal") + node.set_font_size(15) + node.move_to( + max(prevyear[1] + 200, min(width/2-23, nowyear[1]- 200)), + 15, + ) + node.show_text(prevyear[0]) + + UI_color.set(node, win, "dark_overdrop") + UI_elements.roundrect(node, win, + 5+prevmonth[1]+2, + 22, + nowmonth[1]-prevmonth[1]-4 + 50, + 20, + 10) + + UI_color.set(node, win, "text_normal") + node.set_font_size(15) + node.move_to( + max(prevmonth[1] + 12, min(width/2-12, nowmonth[1]- 35)), + 38, + ) + node.show_text(prevmonth[0]) + + # Outputting the layer + layer.set_source_surface(graphsurface, x, y) + layer.paint() + + # Scroll + UI_elements.scroll_area(layer, win, "days", + x, + y, + width, + height+30, + current_X, + bar=True, + mmb=True, + sideways=True) + + + ############## CHECKLIST ################ + + if win.current["tool"] == "schedule": + + path, back, cur, schedulepath = win.current["grab_data"] + + print(schedulepath) + + checklist.draw(layer, win, path, back) + + UI_color.set(layer, win, "node_background") + UI_elements.roundrect(layer, win, + win.current["mx"], + win.current["my"], + 40, + 40, + 10) + + UI_elements.image(layer, win, + "settings/themes/"+win.settings["Theme"]+"/icons/schedule.png", + win.current["mx"], + win.current["my"], + 40, + 40) + + + + elif os.path.exists(win.project+"/set/project.progress"): + checklist.draw(layer, win, win.project+"/set/project.progress", back=win.url) + + elif os.path.exists(win.project+"/project.progress"): + checklist.draw(layer, win, win.project+"/project.progress", back=win.url) + + UI_color.set(layer, win, "node_background") + UI_elements.roundrect(layer, win, + 10, + 10, + win.current["w"]/4-20, + 50, + 10) + + schedule.draw(layer, win) + + # 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"]-50, + 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() + + + return surface diff --git a/studio/studio_assetLayer.py b/studio/studio_assetLayer.py index e911c08..47a97a4 100644 --- a/studio/studio_assetLayer.py +++ b/studio/studio_assetLayer.py @@ -29,6 +29,7 @@ from studio import story from studio import checklist from studio import analytics from studio import studio_dialogs +from studio import schedule def layer(win): @@ -188,6 +189,41 @@ def layer(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, @@ -669,7 +705,7 @@ def layer(win): ############## CHECKLIST ################ if os.path.exists(win.project+"/dev/"+win.cur+"/asset.progress"): - checklist.draw(layer, win, 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. @@ -682,6 +718,63 @@ def layer(win): "/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 = ["vse", "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"] = "vse" # 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) + + # CANCEl def do(): diff --git a/studio/studio_gtk.py b/studio/studio_gtk.py index c1926d7..49da9e6 100644 --- a/studio/studio_gtk.py +++ b/studio/studio_gtk.py @@ -9,6 +9,7 @@ import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk import cairo +import datetime # Own modules @@ -18,6 +19,7 @@ from project_manager import pm_project from studio import studio_storyLayer from studio import studio_settingsLayer from studio import studio_assetLayer +from studio import studio_analyticsLayer # Studio from studio import analytics @@ -119,6 +121,10 @@ def run(project, win): win.current["draw_dot"] = "end" win.current["calls"] = {} # Calls. See sutdio/studio_dialogs.py + new_date_format = "%Y/%m/%d" + today = datetime.datetime.strftime(datetime.datetime.today(), new_date_format) + win.current["date"] = today # Don't even ask. I'm litteraly tired already. + previous(win) # Version of the software @@ -242,9 +248,13 @@ def pmdrawing(pmdrawing, main_layer, win): elif win.url == "settings_layer": Layers.append([studio_settingsLayer.layer(win),"settings_layer"]) + elif win.url == "analytics": + Layers.append([studio_analyticsLayer.layer(win),"analytics"]) + elif win.url == "assets": Layers.append([studio_assetLayer.layer(win),"assets"]) - + + # Call layers. See studio/studio_dialogs.py for explanation. It's wild. @@ -276,10 +286,16 @@ def pmdrawing(pmdrawing, main_layer, win): if len(layer) > 1: layer, url = layer blur = UI_elements.animate(url+"_blur", win, 50) - if win.url != url or win.calllayer: - blur = UI_elements.animate(url+"_blur", win, blur, 50, 2, True) + if (win.url != url or win.calllayer): + if win.current["tool"] not in ["schedule", "grab"]: + blur = UI_elements.animate(url+"_blur", win, blur, 50, 2, True) + else: + blur = UI_elements.animate(url+"_blur", win, 50, 50, 0, True) else: - blur = UI_elements.animate(url+"_blur", win, blur, 0, 2, True) + if win.current["tool"] not in ["schedule", "grab"]: + blur = UI_elements.animate(url+"_blur", win, blur, 0, 2, True) + else: + blur = UI_elements.animate(url+"_blur", win, 0, 0, 0, True) layer = UI_elements.blur(layer, win, blur) else: layer = layer[0] diff --git a/studio/studio_storyLayer.py b/studio/studio_storyLayer.py index ab4fdbd..dbda61d 100644 --- a/studio/studio_storyLayer.py +++ b/studio/studio_storyLayer.py @@ -71,7 +71,9 @@ def layer(win): # Hude analytics button on the top def do(): - print("Analytics") + win.cur = "" + win.url = "analytics" + UI_elements.roundrect(layer, win, 5, @@ -480,9 +482,14 @@ def layer(win): 10, do, "chr", - talk.text("chr"), + talk.text("chr")+"\n[Shift-C]", url="story_editor") + # Shortcut + if 67 in win.current["keys"] and not win.textactive: + do() + win.current["keys"] = [] + # Vehicles def do(): studio_dialogs.asset_select(win, "new_asset_story", select_character, force=True, cur="veh") @@ -495,9 +502,14 @@ def layer(win): 10, do, "veh", - talk.text("veh"), + talk.text("veh")+"\n[Shift-V]", url="story_editor") + # Shortcut + if 86 in win.current["keys"] and not win.textactive: + do() + win.current["keys"] = [] + # Locations def do(): studio_dialogs.asset_select(win, "new_asset_story", select_character, force=True, cur="loc") @@ -510,9 +522,14 @@ def layer(win): 10, do, "loc", - talk.text("loc"), + talk.text("loc")+"\n[Shift-L]", url="story_editor") + # Shortcut + if 76 in win.current["keys"] and not win.textactive: + do() + win.current["keys"] = [] + # Other (obj) def do(): studio_dialogs.asset_select(win, "new_asset_story", select_character, force=True, cur="obj") @@ -525,9 +542,14 @@ def layer(win): 10, do, "obj", - talk.text("obj"), + talk.text("obj")+"\n[Shift-O]", url="story_editor") + # Shortcut + if 79 in win.current["keys"] and not win.textactive: + do() + win.current["keys"] = [] + # Sounds / Music def do(): print("Sounds / Music")