studio_renderLayer.py 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042
  1. # THIS FILE IS A PART OF VCStudio
  2. # PYTHON 3
  3. import os
  4. import datetime
  5. import json
  6. import time
  7. from subprocess import *
  8. # GTK module ( Graphical interface
  9. import gi
  10. gi.require_version('Gtk', '3.0')
  11. from gi.repository import Gtk
  12. from gi.repository import GLib
  13. from gi.repository import Gdk
  14. import cairo
  15. # Own modules
  16. from settings import settings
  17. from settings import talk
  18. from settings import fileformats
  19. from settings import oscalls
  20. from project_manager import pm_project
  21. #UI modules
  22. from UI import UI_elements
  23. from UI import UI_color
  24. from UI import UI_math
  25. # Studio
  26. from studio import studio_dialogs
  27. from studio import analytics
  28. from studio import story
  29. # Network / Rendering
  30. from network import network_renders
  31. def getnumstr(num):
  32. # This function turns numbers like 1 or 20 into numbers like 0001 or 0020
  33. s = ""
  34. for i in range(4-len(str(num))):
  35. s = s + "0"
  36. return s+str(num)
  37. def getfileoutput(num, FORMAT):
  38. # Function gives an output of a file. From the current frame that's rendering.
  39. # instead of having frame 1 and format EXR it will give you 0001.exr
  40. s = getnumstr(num)
  41. if FORMAT == "JPEG":
  42. s = s + ".jpg"
  43. else:
  44. s = s + "." + FORMAT.lower()
  45. return s
  46. def save_settings(win, filename):
  47. ############################################################################
  48. # This function will save the render settings file.
  49. ############################################################################
  50. folder = filename[:filename.rfind("/")]+"/extra"
  51. savefile = folder+filename[filename.rfind("/"):]+".json"
  52. # First let's make sure that the extra folder exists.
  53. try:
  54. os.makedirs(win.project+folder)
  55. except:
  56. pass
  57. # Then let's write the file in there.
  58. with open(win.project+savefile, 'w') as fp:
  59. json.dump(win.renders[filename], fp, sort_keys=True, indent=4)
  60. def layer(win, call):
  61. ##########################################################################
  62. # This file will setup and manage rendering of shots. It's a bit complex
  63. # in function. I have 2 network scripts at the moment. And it might grow
  64. # beyond that.
  65. # See:
  66. # network/during_render.py
  67. # network/network_renders.py
  68. # This file is the UI part of the process. ( The main UI ) Some beats and
  69. # peaces will exists in various windows through out the program.
  70. ##########################################################################
  71. # Making the layer
  72. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, win.current['w'],
  73. win.current['h'])
  74. layer = cairo.Context(surface)
  75. #text setting
  76. layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
  77. UI_color.set(layer, win, "dark_overdrop")
  78. layer.rectangle(
  79. 0,
  80. 0,
  81. win.current["w"],
  82. win.current["h"],
  83. )
  84. layer.fill()
  85. #########################################################################
  86. #
  87. # THE RENDER PREVIEW THINGY
  88. #
  89. #########################################################################
  90. # Here I want to do a preview that's going to be rendered on the right side
  91. try:
  92. the_render = win.renders[list(win.renders.keys())[0]]
  93. amount_of_frames = the_render["end_frame"] - the_render["start_frame"] + 1
  94. passed = time.time() - win.render_runtime.get("started_rendering", 0)
  95. passed = passed*1000000
  96. if win.render_runtime.get("to_render"):
  97. longest = passed
  98. else:
  99. longest = 0
  100. save_folder = the_render["save_folder"]
  101. for i in the_render["analytics"][save_folder].values():
  102. if i > longest:
  103. longest = i
  104. shortest = longest
  105. for i in the_render["analytics"][save_folder].values():
  106. if i < shortest:
  107. shortest = i
  108. rn_files = []
  109. preview_image = "horse"
  110. render = list(win.renders.keys())[0]
  111. folder = render[:render.rfind("/")]
  112. if "render_preview_hovering" not in win.current:
  113. win.current["render_preview_hovering"] = False
  114. write_hovering = False
  115. for n, frame in enumerate(range(the_render["start_frame"], the_render["end_frame"]+1)):
  116. f = getfileoutput(frame, the_render["image_format"])
  117. rn_image = win.project+folder+"/"+save_folder+"/"+f
  118. if os.path.exists(rn_image):
  119. rn_files.append(f)
  120. for n, frame in enumerate(range(the_render["start_frame"], the_render["end_frame"]+1)):
  121. frame_w = (win.current["w"]-630)/amount_of_frames
  122. frame_h = (win.current["h"]-200)/3
  123. try:
  124. frac = the_render["analytics"][save_folder][str(frame)] / longest
  125. frame_h = frame_h * frac
  126. except Exception as e:
  127. frame_h = 0
  128. if win.render_runtime.get("current_frame", 0) == frame and win.render_runtime.get("to_render"):
  129. frame_h = (win.current["h"]-200)/3 * (passed / longest)
  130. frame_h = max(frame_h, frame_w)
  131. minus_w = 2
  132. round_r = int((frame_w-2)/2)
  133. if frame_w < 5:
  134. minus_w = -1
  135. round_r = 0
  136. f = getfileoutput(frame, the_render["image_format"])
  137. UI_color.set(layer, win, "node_background")
  138. if f not in rn_files:
  139. UI_color.set(layer, win, "dark_overdrop")
  140. if the_render["analytics"][save_folder].get(str(frame)) == shortest:
  141. UI_color.set(layer, win, "node_blendfile")
  142. if win.render_runtime.get("current_frame", 0) == frame and win.render_runtime.get("to_render"):
  143. UI_color.set(layer, win, "progress_active")
  144. if the_render["analytics"][save_folder].get(str(frame)) == longest:
  145. UI_color.set(layer, win, "node_badfile")
  146. if int(win.current["frame"]/2) % (len(rn_files)) == frame\
  147. and not win.current["render_preview_hovering"]:
  148. UI_color.set(layer, win, "progress_active")
  149. if 580+(n*frame_w) < win.current["mx"] < 580+(n*frame_w)+frame_w:
  150. UI_color.set(layer, win, "progress_active")
  151. preview_image = getfileoutput(frame, the_render["image_format"])
  152. if str(frame) in the_render["analytics"][save_folder]:
  153. write_hovering = True
  154. UI_elements.tooltip(win, "Fra:"+str(frame)+"\n"+f+"\n"+UI_math.timestring(the_render["analytics"][save_folder].get(str(frame))/1000000))
  155. elif not write_hovering:
  156. write_hovering = False
  157. UI_elements.roundrect(layer, win,
  158. 580+(n*frame_w),
  159. 100+(win.current["h"]-200-frame_h),
  160. frame_w-minus_w,
  161. frame_h,
  162. int((frame_w-2)/2))
  163. win.current["render_preview_hovering"] = write_hovering
  164. rn_image = win.project+folder+"/"+save_folder+"/"+preview_image
  165. # CLIP
  166. UI_elements.roundrect(layer, win,
  167. 580,
  168. 100,
  169. win.current["w"] - 630,
  170. int((win.current["h"]-200)/3*1.9),
  171. 10,
  172. fill=False)
  173. layer.clip()
  174. if os.path.exists(rn_image):
  175. UI_elements.image(layer, win, rn_image,
  176. 580,
  177. 100,
  178. win.current["w"] - 630,
  179. int((win.current["h"]-200)/3*1.9),
  180. cell="render_preview")
  181. else:
  182. n = int(win.current["frame"]/2) % (len(rn_files))
  183. f = rn_files[n]
  184. rn_image = win.project+folder+"/"+save_folder+"/"+f
  185. UI_elements.image(layer, win, rn_image,
  186. 580,
  187. 100,
  188. win.current["w"] - 630,
  189. int((win.current["h"]-200)/3*1.9),
  190. cell="render_preview")
  191. layer.reset_clip()
  192. if win.render_runtime.get("to_render"):
  193. UI_color.set(layer, win, "text_normal")
  194. layer.set_font_size(20)
  195. layer.move_to(50,
  196. win.current["h"]-30)
  197. layer.show_text(win.render_runtime.get("current_progress", ""))
  198. except Exception as e:
  199. print(e)
  200. #########################################################################
  201. #
  202. # THE RENDER MENU
  203. #
  204. #########################################################################
  205. UI_color.set(layer, win, "node_background")
  206. UI_elements.roundrect(layer, win,
  207. 310-250,
  208. 100,
  209. 500,
  210. win.current["h"]-200,
  211. 10)
  212. # Documentation entry
  213. def do():
  214. def after(win, var):
  215. pass
  216. studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_render"))
  217. UI_elements.roundrect(layer, win,
  218. 310-250,
  219. win.current["h"]-140,
  220. 40,
  221. 40,
  222. 10,
  223. do,
  224. "question")
  225. is_rendering = False # This will determen whether
  226. for render in win.renders:
  227. if win.renders[render]["rendering"]:
  228. is_rendering = True
  229. if not is_rendering:
  230. # Render button
  231. def do():
  232. # This here will launch a script that will be on it's on from now
  233. # on. See:
  234. # network/during_render.py
  235. try:
  236. with open(win.project+"/render_runtime.json") as json_file:
  237. runtime = json.load(json_file)
  238. except:
  239. runtime = {}
  240. runtime["to_render"] = True
  241. with open(win.project+"/render_runtime.json", 'w') as fp:
  242. json.dump(runtime, fp, indent=4)
  243. Popen(["python3", "network/during_render.py", win.project, oscalls.get_current_blender(win)])
  244. UI_elements.roundrect(layer, win,
  245. 310-20,
  246. win.current["h"]-140,
  247. 40,
  248. 40,
  249. 10,
  250. button=do,
  251. icon="right")
  252. else:
  253. # Stop Render button
  254. def do():
  255. network_renders.stop_render(win)
  256. UI_elements.roundrect(layer, win,
  257. 310-20,
  258. win.current["h"]-140,
  259. 40,
  260. 40,
  261. 10,
  262. button=do,
  263. icon="stop")
  264. def do():
  265. os.system("gnome-terminal -- python3 "+os.getcwd()+"/network/render_viewer.py "+win.project)
  266. UI_elements.roundrect(layer, win,
  267. 310-20-40,
  268. win.current["h"]-140,
  269. 40,
  270. 40,
  271. 10,
  272. button=do,
  273. icon="analytics")
  274. # Exit button
  275. def do():
  276. win.current["calls"][call]["var"] = False
  277. UI_elements.roundrect(layer, win,
  278. 100+420,
  279. win.current["h"]-140,
  280. 40,
  281. 40,
  282. 10,
  283. button=do,
  284. icon="cancel",
  285. tip=talk.text("cancel"),
  286. url="render")
  287. x = 70
  288. y = 100 + 10
  289. width = 500 - 20
  290. height = win.current["h"]-200 - 20
  291. UI_elements.roundrect(layer, win,
  292. x,
  293. y,
  294. width,
  295. height-60,
  296. 10,
  297. fill=False)
  298. layer.clip()
  299. clip = [
  300. x,
  301. y,
  302. width,
  303. height-60]
  304. # Let's get the filename of the current file that we want to setup.
  305. filename = win.current["renders_window"]["filename"]
  306. # So this window could be accessed from both main window and from the script.
  307. # One is to see where each render is. And ther other is to configure and add
  308. # renders to the list.
  309. if filename:
  310. # Basically if any file is inputted. It means that it's to configure.
  311. # but sometimes. ( I'm tyring to see myself using it ) the user will
  312. # click on the render button to just access the render. And to hell with
  313. # it. Let's make the filename variable the selection of the file.
  314. if filename not in win.renders:
  315. # So on the initialization we want to do a couple of things.
  316. # First of which will be to get render settings data from the
  317. # blend file.
  318. # I think a quick bpy script could do.
  319. # See:
  320. # studio/bpy_get_render_settings.py
  321. blenderpath = oscalls.get_current_blender(win)
  322. blend = win.project+filename
  323. checkframes = Popen([blenderpath, "-b", blend , "-P",
  324. os.getcwd()+"/studio/bpy_get_render_settings.py"],stdout=PIPE, universal_newlines=True)
  325. checkframes.wait()
  326. checkstring = checkframes.stdout.read()
  327. # We are going to need the following options.
  328. start_frame = 0
  329. end_frame = 250
  330. image_format = "PNG"
  331. save_folder = "storyboard"
  332. for line in checkstring.split("\n"):
  333. if line.startswith("Start_frame"):
  334. try:
  335. start_frame = int(line[line.find(":")+1:])
  336. except:
  337. pass
  338. if line.startswith("End_frame"):
  339. try:
  340. end_frame = int(line[line.find(":")+1:])
  341. except:
  342. pass
  343. # Now since we've got the data. Let's write it to the dictionary first.
  344. win.renders[filename] = {
  345. "start_frame" :start_frame , # The frame we want to start on
  346. "end_frame" :end_frame , # The frame we want to end on
  347. "image_format" :image_format, # What format to save the images
  348. "save_folder" :save_folder , # Into what folder to save images
  349. "clean_folder" :False , # Whether to delete current frames before rendering
  350. "current_frame":0 , # What is current frame rendering
  351. "rendering" :False , # Whether it's currently rendering
  352. "analytics" :{} # Times of each frame
  353. }
  354. # Now in order not to loose the data immediatly. We are going to need
  355. # to add the filename into a special file.
  356. s = open(win.project+"/set/active_renders.data", "a")
  357. s.write(filename+"\n")
  358. s.close()
  359. # Also we want to make a little json file in the extra folder of
  360. # the shot. This will contain our settings. And the file will be
  361. # read by the renderer script while it's running. It has to be on
  362. # it's own. So it's good to have extendable files.
  363. save_settings(win, filename)
  364. # Now let's get to the actuall UI of the stuff. Basically I want it to
  365. # always give us a list of the currently set renders. And one of them
  366. # might be selected and expendet to see they settings / current data.
  367. # Setting up the scroll
  368. if "render" not in win.scroll:
  369. win.scroll["render"] = 0
  370. current_Y = 0
  371. # So let's do this.
  372. is_rendering = False # This will determen whether
  373. # Before we dive into settings and graphs. Let's make a deletion
  374. if 65535 in win.current["keys"] and not win.renders[win.current["renders_window"]["filename"]]["rendering"]:
  375. try:
  376. del win.renders[win.current["renders_window"]["filename"]]
  377. active_renders = open(win.project+"/set/active_renders.data")
  378. active_renders = active_renders.read()
  379. active_renders = active_renders.split("\n")
  380. s = open(win.project+"/set/active_renders.data", "w")
  381. for i in active_renders:
  382. if i != win.current["renders_window"]["filename"] and i:
  383. s.write(i+"\n")
  384. s.close()
  385. except:
  386. pass
  387. win.current["renders_window"]["filename"] = ""
  388. win.current["keys"] = []
  389. for render in win.renders:
  390. # Let's get a shot name for each render.
  391. shot = render[:render.rfind("/")].replace("/rnd", "")
  392. blend = render[render.rfind("/"):]
  393. tip = (shot+blend).replace("/", "", 1).replace("/", " | ")
  394. if win.renders[render]["rendering"]:
  395. tip = win.renders[render]["rendering"]
  396. def do():
  397. win.current["renders_window"]["filename"] = render
  398. UI_elements.roundrect(layer, win,
  399. x,
  400. y+current_Y+win.scroll["render"],
  401. width,
  402. 80,
  403. 10,
  404. button=do,
  405. tip=tip,
  406. clip=clip)
  407. # I think the render logo could be cool.
  408. UI_elements.image(layer, win,
  409. "settings/themes/"+win.settings["Theme"]+"/icons/render.png",
  410. x+5, y+current_Y+win.scroll["render"]+5, 40, 40)
  411. # And the name of the shot
  412. UI_color.set(layer, win, "text_normal")
  413. layer.set_font_size(20)
  414. layer.move_to(x+60,
  415. y+current_Y + win.scroll["render"]+30)
  416. layer.show_text((shot+blend).replace("/", "", 1).replace("/", " | "))
  417. # And let's draw the fraction
  418. fraction = (win.renders[render]["current_frame"] - win.renders[render]["start_frame"])\
  419. / (win.renders[render]["end_frame"] - win.renders[render]["start_frame"])
  420. fraction = max(0,min(1, fraction))
  421. if fraction:
  422. UI_color.set(layer, win, "progress_background")
  423. UI_elements.roundrect(layer, win,
  424. x+20,
  425. y+current_Y + win.scroll["render"]+55,
  426. width-40,
  427. 0,
  428. 5)
  429. UI_color.set(layer, win, "progress_active")
  430. UI_elements.roundrect(layer, win,
  431. x+20,
  432. y+current_Y + win.scroll["render"]+55,
  433. (width-40)*fraction,
  434. 0,
  435. 5)
  436. # Now selection. When you click on one you expend it. And you can see
  437. # what settings are inside.
  438. if win.current["renders_window"]["filename"] == render:
  439. UI_color.set(layer, win, "progress_background")
  440. UI_elements.roundrect(layer, win,
  441. x,
  442. y+current_Y+win.scroll["render"],
  443. width,
  444. 80,
  445. 10,
  446. fill=False)
  447. layer.stroke()
  448. current_Y = current_Y + 90
  449. # We are going to have 2 differnt UI's for those who are rendering
  450. # and those who are not rendering.
  451. if not win.renders[render]["rendering"]:
  452. # We need to choose the folder of where the given render will be done.
  453. # For the user tho I don't think nessesary to understand that it's a
  454. # folder nessesary. They will see 4 circles. 4 render qualities. And
  455. # will decide stuff based on that.
  456. fouricons = [ "storyboard", "opengl", "test_rnd", "rendered"]
  457. for num, f in enumerate(fouricons):
  458. if f == win.renders[render]["save_folder"]:
  459. UI_color.set(layer, win, "progress_time")
  460. UI_elements.roundrect(layer, win,
  461. x+20+(40*num),
  462. y+current_Y + win.scroll["render"],
  463. 40,
  464. 40,
  465. 10)
  466. def do():
  467. win.renders[render]["save_folder"] = f
  468. save_settings(win, render)
  469. UI_elements.roundrect(layer, win,
  470. x+20+(40*num),
  471. y+current_Y + win.scroll["render"],
  472. 40,
  473. 40,
  474. 10,
  475. button=do,
  476. icon=f,
  477. tip=f)
  478. # Here also I want to have a little clean icon. This will make sure
  479. # to clean the current frames that are currently inside the folder.
  480. # I know it's slightly counter intuitive compared to the rest of
  481. # the program in that it's better not to give the user the ability
  482. # to delete any actuall files. But in this case it makes a bit
  483. # sense. Since for the user rendering again is like replacing the
  484. # previous files with new ones. And the algorythm always renders
  485. # from the last frame in the folder. So it never deletes anything
  486. # by defaul. So I guess a button to clean frames could be a good
  487. # thing.
  488. if win.renders[render]["clean_folder"]:
  489. UI_color.set(layer, win, "progress_time")
  490. UI_elements.roundrect(layer, win,
  491. x+width-40,
  492. y+current_Y + win.scroll["render"],
  493. 40,
  494. 40,
  495. 10)
  496. def do():
  497. win.renders[render]["clean_folder"] = not win.renders[render]["clean_folder"]
  498. save_settings(win, render)
  499. UI_elements.roundrect(layer, win,
  500. x+width-40,
  501. y+current_Y + win.scroll["render"],
  502. 40,
  503. 40,
  504. 10,
  505. button=do,
  506. icon="clean",
  507. tip=talk.text("clean_render_folder"))
  508. current_Y = current_Y + 50
  509. # Now let's put the settings them selves.
  510. # First thing is we need start and end frames. And we also need it
  511. # somehow readable for the user.
  512. # I will probably need this.
  513. def is_number(string):
  514. try:
  515. int(string)
  516. return True
  517. except:
  518. return False
  519. # START FRAME
  520. UI_elements.text(layer, win, render+"start_frame",
  521. x+10,
  522. y+current_Y+win.scroll["render"],
  523. 100,
  524. 40,
  525. set_text=str(win.renders[render]["start_frame"]),
  526. tip=talk.text("rendering_start_frame"))
  527. if win.text[render+"start_frame"]["text"] != str(win.renders[render]["start_frame"])\
  528. and is_number(win.text[render+"start_frame"]["text"]):
  529. def do():
  530. win.renders[render]["start_frame"] = int(win.text[render+"start_frame"]["text"])
  531. save_settings(win, render)
  532. UI_elements.roundrect(layer, win,
  533. x+70,
  534. y+current_Y+win.scroll["render"],
  535. 40,
  536. 40,
  537. 10,
  538. button=do,
  539. icon="ok",
  540. tip=talk.text("checked"))
  541. # FILE FORMATS
  542. # I will not add any video files since the algoryhm will require
  543. # actuall frames to be stored as separate files. This will insure
  544. # that ALL frames were rendered. And in case of crash the algorythm
  545. # will pick up from a previous frame in a folder. With video is not
  546. # only impossible. But in case of a crash with video all the previous
  547. # frames will be lost.
  548. # What I want to do is to ejucate the user on the Open Formats.
  549. # You know using OGG video instead of MP4 and stuff like that.
  550. # For images there are the same types of Open Formats. The point
  551. # is that these formats could be made and played using entirelly
  552. # free software.
  553. # I will let selection of formats that are not in the list but I
  554. # will mark them as non-recommended. I will do it like so.
  555. # [ <start frame> ] [ PNG ] [ <png frame>
  556. # V PNG ?
  557. # V JPEG ?
  558. # V EXR ?
  559. # X HDR ?
  560. # And so on and forth. You can see that HDR is marked with an X
  561. # it will be a button linking to the :
  562. linfo = "http://www.linfo.org/free_file_format.html"
  563. # so the user could read a full document describing desisions
  564. # about the formats.
  565. formats = {
  566. "PNG" : [True, "Image PNG", "https://en.wikipedia.org/wiki/Portable_Network_Graphics"],
  567. "JPEG": [True, "Image JPEG", "https://en.wikipedia.org/wiki/JPEG"],
  568. "EXR" : [True, "Open EXR", "https://en.wikipedia.org/wiki/OpenEXR"],
  569. "HDR" : [False,"Radiance HDR", "https://en.wikipedia.org/wiki/RGBE_image_format"],
  570. "BMP" : [False,"Microsoft BMP", "https://en.wikipedia.org/wiki/BMP_file_format"],
  571. "TGA" : [False,"Truevision TGA", "https://en.wikipedia.org/wiki/Truevision_TGA"],
  572. "TIFF": [False,"Tiff", "https://en.wikipedia.org/wiki/Tagged_Image_File_Format"]
  573. }
  574. if "selecting_render_file_format" not in win.current:
  575. win.current["selecting_render_file_format"] = False
  576. def do():
  577. win.current["selecting_render_file_format"] = not win.current["selecting_render_file_format"]
  578. if win.current["selecting_render_file_format"]:
  579. win.scroll["render"] = win.scroll["render"]-50*len(formats)
  580. win.current["LMB"] = False
  581. win.previous["LMB"] = False
  582. UI_elements.roundrect(layer, win,
  583. x+120,
  584. y+current_Y + win.scroll["render"],
  585. 235,
  586. 40,
  587. 10,
  588. button=do,
  589. tip=talk.text("rendering_file_format"))
  590. currentformat = win.renders[render]["image_format"]
  591. if not win.current["selecting_render_file_format"]:
  592. UI_color.set(layer, win, "text_normal")
  593. layer.set_font_size(20)
  594. layer.move_to(310-len(formats[currentformat][1])*6,
  595. y+current_Y + win.scroll["render"]+30)
  596. layer.show_text(formats[currentformat][1])
  597. # END FRAME
  598. UI_elements.text(layer, win, render+"end_frame",
  599. x+365,
  600. y+current_Y+win.scroll["render"],
  601. 100,
  602. 40,
  603. set_text=str(win.renders[render]["end_frame"]),
  604. tip=talk.text("rendering_end_frame"))
  605. if win.text[render+"end_frame"]["text"] != str(win.renders[render]["end_frame"])\
  606. and is_number(win.text[render+"end_frame"]["text"]):
  607. def do():
  608. win.renders[render]["end_frame"] = int(win.text[render+"end_frame"]["text"])
  609. save_settings(win, render)
  610. UI_elements.roundrect(layer, win,
  611. x+215+210,
  612. y+current_Y+win.scroll["render"],
  613. 40,
  614. 40,
  615. 10,
  616. button=do,
  617. icon="ok",
  618. tip=talk.text("checked"))
  619. current_Y = current_Y + 50
  620. if win.current["selecting_render_file_format"]:
  621. for num, f in enumerate(formats):
  622. if f == win.renders[render]["image_format"]:
  623. UI_color.set(layer, win, "progress_time")
  624. UI_elements.roundrect(layer, win,
  625. x+120,
  626. y+current_Y + win.scroll["render"],
  627. 235,
  628. 40,
  629. 10)
  630. def do():
  631. win.renders[render]["image_format"] = f
  632. save_settings(win, render)
  633. win.current["selecting_render_file_format"] = False
  634. UI_elements.roundrect(layer, win,
  635. x+120,
  636. y+current_Y + win.scroll["render"],
  637. 235,
  638. 40,
  639. 10,
  640. button=do)
  641. UI_color.set(layer, win, "text_normal")
  642. layer.set_font_size(20)
  643. layer.move_to(310-len(formats[f][1])*6,
  644. y+current_Y + win.scroll["render"]+30)
  645. layer.show_text(formats[f][1])
  646. # RECCOMENDATION
  647. if formats[f][0]:
  648. rec = talk.text("recommended_yes")
  649. ic = "ok"
  650. else:
  651. rec = talk.text("recommended_not")
  652. ic = "cancel"
  653. def do():
  654. os.system("xdg-open "+linfo)
  655. UI_elements.roundrect(layer, win,
  656. x+10,
  657. y+current_Y + win.scroll["render"],
  658. 40,
  659. 40,
  660. 10,
  661. button=do,
  662. icon=ic,
  663. tip=rec)
  664. # WIKIPEDIA ABOUT THE FORMAT
  665. def do():
  666. os.system("xdg-open "+formats[f][-1])
  667. UI_elements.roundrect(layer, win,
  668. x+430,
  669. y+current_Y + win.scroll["render"],
  670. 40,
  671. 40,
  672. 10,
  673. button=do,
  674. icon="question",
  675. tip="Wikipedia")
  676. current_Y = current_Y + 50
  677. else:
  678. # And here comes the UI of when it's during RENDERING
  679. # First thing will be to draw a little graph. This will show current
  680. # frame and analytics data about render times.
  681. UI_color.set(layer, win, "dark_overdrop")
  682. layer.rectangle(
  683. x+5,
  684. y+current_Y+win.scroll["render"],
  685. width-10,
  686. 100)
  687. layer.fill()
  688. for frame in range(win.renders[render]["start_frame"], win.renders[render]["end_frame"]+1):
  689. numofis = win.renders[render]["end_frame"] - win.renders[render]["start_frame"]
  690. if frame == win.renders[render]["current_frame"]:
  691. UI_color.set(layer, win, "progress_time")
  692. layer.rectangle(
  693. x+5+(width-10)/numofis*(frame-1),
  694. y+current_Y+win.scroll["render"],
  695. (width-10)/numofis,
  696. 100)
  697. layer.fill()
  698. # Now I want to be able to interact with the graph. For this I want to
  699. # add a little mouse sensitive region. That will give me data about the
  700. # current frame. In a tooltip?
  701. if int(win.current["mx"]) in range(int(x+5+(width-10)/numofis*frame),int(x+5+(width-10)/numofis*frame+(width-10)/numofis))\
  702. and int(win.current["my"]) in range(int(y+current_Y+win.scroll["render"]), int(y+current_Y+win.scroll["render"]+100)):
  703. UI_color.set(layer, win, "progress_background")
  704. layer.move_to(x+5+(width-10)/numofis*(frame-0.5),
  705. y+current_Y+win.scroll["render"]
  706. )
  707. layer.line_to(
  708. x+5+(width-10)/numofis*(frame-0.5),
  709. y+current_Y+win.scroll["render"]+100
  710. )
  711. layer.stroke()
  712. if win.renders[render]["save_folder"] in win.renders[render]["analytics"]:
  713. if str(frame) in win.renders[render]["analytics"][win.renders[render]["save_folder"]]:
  714. value = win.renders[render]["analytics"][win.renders[render]["save_folder"]][str(frame)]
  715. UI_elements.tooltip(win, UI_math.timestring(value/1000000))
  716. # Now let's draw a graph. I love graphs. They are cool AF. First of all tho
  717. # we need to know the maximum value. Because who know how long was the render
  718. mx = 0
  719. allvalues = []
  720. if win.renders[render]["save_folder"] in win.renders[render]["analytics"]:
  721. for v in win.renders[render]["analytics"][win.renders[render]["save_folder"]]:
  722. mx = max(win.renders[render]["analytics"][win.renders[render]["save_folder"]][v], mx)
  723. allvalues.append(win.renders[render]["analytics"][win.renders[render]["save_folder"]][v])
  724. UI_color.set(layer, win, "progress_background")
  725. layer.move_to(x+5, y+current_Y+win.scroll["render"]+100)
  726. for frame in range(win.renders[render]["start_frame"], win.renders[render]["end_frame"]+1):
  727. numofis = win.renders[render]["end_frame"] - win.renders[render]["start_frame"]
  728. if win.renders[render]["save_folder"] in win.renders[render]["analytics"]:
  729. if str(frame) in win.renders[render]["analytics"][win.renders[render]["save_folder"]]:
  730. value = win.renders[render]["analytics"][win.renders[render]["save_folder"]][str(frame)]
  731. layer.line_to(
  732. x+5+(width-10)/numofis*(frame-0.5),
  733. (y+current_Y+win.scroll["render"]+100)-(100/mx*value)
  734. )
  735. layer.stroke()
  736. current_Y = current_Y + 110
  737. # Now let's put out some data in the text format. For the user to
  738. # know stuff he or she might want to know.
  739. # AVARAGE TIME
  740. avarage = 0
  741. try:
  742. avarage = sum(allvalues) / len(allvalues)
  743. except:
  744. pass
  745. UI_color.set(layer, win, "text_normal")
  746. layer.set_font_size(20)
  747. layer.move_to(x+10,
  748. y+current_Y + win.scroll["render"]+30)
  749. layer.show_text(talk.text("render_avarage_time")+" "+UI_math.timestring(avarage/1000000))
  750. current_Y = current_Y + 40
  751. # REMAINING TIME
  752. remaining = avarage * (win.renders[render]["end_frame"] - win.renders[render]["current_frame"] + 1)
  753. remaining = remaining/1000000
  754. remaining = remaining - (time.time() - win.render_runtime.get("started_rendering", 0))
  755. UI_color.set(layer, win, "text_normal")
  756. layer.set_font_size(20)
  757. layer.move_to(x+10,
  758. y+current_Y + win.scroll["render"]+30)
  759. layer.show_text(talk.text("render_remaining_time")+" "+UI_math.timestring(remaining))
  760. current_Y = current_Y + 40
  761. else:
  762. current_Y = current_Y + 85
  763. ###########################
  764. UI_elements.scroll_area(layer, win, "render",
  765. x,
  766. y,
  767. width,
  768. height-60,
  769. current_Y,
  770. bar=True,
  771. mmb=True,
  772. url="render"
  773. )
  774. return surface