# 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 from studio import history def layer(win): # Making the layer surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, win.current['w'], win.current['h']) layer = cairo.Context(surface) #text setting layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) UI_color.set(layer, win, "dark_overdrop") layer.rectangle( 0, 0, win.current["w"], win.current["h"], ) layer.fill() UI_color.set(layer, win, "node_background") UI_elements.roundrect(layer, win, win.current["w"]/4, 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/scene.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. # Documentation entry def do(): def after(win, var): pass studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_analytics")) UI_elements.roundrect(layer, win, win.current["w"]/4*3-110, 225, 40, 40, 10, do, "question") # 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,"scene", "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() # Let's force graph to refresh on each click if not win.current["LMB"] and win.previous["LMB"]: try: del win.current["graph_cashe"] except: pass ############### 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"] win.textactive = "" 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 win.textactive = "" 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() # STARTDATE & DEADLINE elif theday in [startdate, deadline]: UI_color.set(node, win, "node_badfile") UI_elements.roundrect(node, win, 5+current_X+win.scroll["days"], 67, 40, height-67, 10, fill=False) node.stroke() # SELECTION BUTTON def do(): if win.current["tool"] == "schedule": # If it's a scheduling. path, back, cur, schedulepath, username = win.current["grab_data"].copy() path = path.replace(win.project, "") path = path[path.find(cur)+len(cur):] if theday not in win.analytics["dates"]: win.analytics["dates"][theday] = {} name = cur[cur.rfind("/")+1:] acur = cur.replace(name, "").replace("/", "") if acur in ["chr", "veh", "loc","obj"]: itemtype = "assets" elif not acur: itemtype = "files" else: itemtype = "scenes" if itemtype not in win.analytics["dates"][theday]: win.analytics["dates"][theday][itemtype] = {} if cur not in win.analytics["dates"][theday][itemtype]: win.analytics["dates"][theday][itemtype][cur] = [] #print("test 1") win.analytics["dates"][theday][itemtype][cur].append( ["00:00:00", "schedule", path, "[Un-Checked]", schedulepath, username] ) #print("test 2") # RETURNING BACK TO NORMAL win.url = back win.current["tool"] = "selection" analytics.save(win.project, win.analytics) win.analytics = analytics.load(win.project) win.checklists = {} #print("test 3") else: 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": # If the tool is scheduling I want to make sure that the user sees where # he needs to place the task. A higlight. UI_color.set(layer, win, "progress_background") UI_elements.roundrect(layer, win, x, y+67, width, height-67, 10, fill=False) layer.stroke() path, back, cur, schedulepath, username = win.current["grab_data"].copy() 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) # In the analytics window there will a choise of whether to see schedules. Or to # see history. if "analytics_left_panel" not in win.current: win.current["analytics_left_panel"] = "schedule" 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(["schedule", "history"]): if win.current["analytics_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["analytics_left_panel"] = thing UI_elements.roundrect(layer, win, 20+(40*num), 15, 40, 40, 10, do, thing) ##### SCHEDULE ###### if win.current["analytics_left_panel"] == "schedule": schedule.draw(layer, win) ##### HISTORY ####### else: history.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