studio_shot_linkLayer.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. # THIS FILE IS A PART OF VCStudio
  2. # PYTHON 3
  3. import os
  4. from subprocess import *
  5. # GTK module ( Graphical interface
  6. import gi
  7. gi.require_version('Gtk', '3.0')
  8. from gi.repository import Gtk
  9. from gi.repository import GLib
  10. from gi.repository import Gdk
  11. import cairo
  12. # Own modules
  13. from settings import settings
  14. from settings import oscalls
  15. from settings import talk
  16. from settings import fileformats
  17. from project_manager import pm_project
  18. #UI modules
  19. from UI import UI_elements
  20. from UI import UI_color
  21. # story
  22. from studio import story
  23. from studio import analytics
  24. from studio import history
  25. from studio import studio_dialogs
  26. from studio import history
  27. def link_from_script(win):
  28. #########################################################################
  29. # This file is here because there are multiple places in the window where
  30. # I want to link assets based on what's written in the text of the script.
  31. # So this file will basically parse the text and add the assets it finds
  32. # linked to the link list.
  33. #########################################################################
  34. print("Reading from the scene")
  35. # Fist we need to know what scene and shot are we.
  36. if win.cur.count("/") > 1:
  37. tmp = win.cur.replace("/","",1).split("/")
  38. scene, shotis = tmp[0], tmp[1]
  39. else:
  40. scene = win.cur[win.cur.rfind("/")+1:]
  41. shotis = ""
  42. print(scene, shotis)
  43. # Now let's read through the scene.
  44. for shot in win.story["scenes"][scene]["shots"]:
  45. # Ignore the non shots.
  46. if shot[0] != "shot_block":
  47. continue
  48. # Ingore all other shots.
  49. if shot[1] != shotis:
  50. continue
  51. # Let's read the shot
  52. for asset in shot[-1]:
  53. # Ignore text
  54. if asset[0] == "text":
  55. continue
  56. # It's it's a staight up link. Just add it.
  57. if asset[0] == "link" and "/dev"+asset[1] not in win.current["linking_asset_data"]["assets"]:
  58. win.current["linking_asset_data"]["assets"].append("/dev"+asset[1])
  59. # It's a link in a frase.
  60. if asset[0] == "frase" and asset[1][0] == "link" and "/dev"+asset[1][1] not in win.current["linking_asset_data"]["assets"]:
  61. win.current["linking_asset_data"]["assets"].append("/dev"+asset[1][1])
  62. def layer(win, call):
  63. # Making the layer
  64. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, win.current['w'],
  65. win.current['h'])
  66. layer = cairo.Context(surface)
  67. #text setting
  68. layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
  69. UI_color.set(layer, win, "dark_overdrop")
  70. layer.rectangle(
  71. 0,
  72. 0,
  73. win.current["w"],
  74. win.current["h"],
  75. )
  76. layer.fill()
  77. UI_color.set(layer, win, "node_background")
  78. UI_elements.roundrect(layer, win,
  79. 40,
  80. 40,
  81. win.current["w"]-80,
  82. win.current["h"]-80,
  83. 10)
  84. ##############################################################################
  85. # This file is configuring and lanunching the linking into blend files. But it's
  86. # not the only file in the chain of files that is responsible for this operation.
  87. # For example the linking it self does studio/bpy_do_linking.py that is running
  88. # inside blender and communicates with this file for the UI side of things.
  89. ##############################################################################
  90. # Okay
  91. if not win.current["linking_asset_data"]["fraction"]:
  92. def do():
  93. # Before we start let's record the history for it.
  94. history.record(win, win.project+win.current["linking_asset_data"]["linking_to"], "[Linked]")
  95. if not win.current["linking_asset_data"]["process"]:
  96. # So this is an apply button. I know it's a little not in the right
  97. # order. So please read the rest of the file to see what it does.
  98. # So now when a person clicks okay. We first need to save the
  99. # autolink.data.
  100. datafile = open(win.project+"/rnd"+win.cur+"/extra/autolink.data", "w")
  101. for asset in win.current["linking_asset_data"]["assets"]:
  102. datafile.write("Link : "+asset+"\n")
  103. # Also let's write the mode. Because it's important.
  104. datafile.write("Mode : "+win.current["linking_asset_data"]["mode"]+"\n")
  105. datafile.close()
  106. # Okay... now that we have the file. We can start the process of linking
  107. # Which will be done using Blender. So let's get blender's url
  108. blenderpath = oscalls.get_current_blender(win)
  109. # Now here is the fun part. We gonna start a background process
  110. # open there blender and will start linking stuff.
  111. win.current["linking_asset_data"]["process"] = Popen(['stdbuf', '-o0', blenderpath, \
  112. "-b", win.project+win.current["linking_asset_data"]["linking_to"], \
  113. "-P", os.getcwd()+"/studio/bpy_do_linking.py"],\
  114. stdout=PIPE, universal_newlines=True)
  115. win.current["linking_asset_data"]["fraction"] = 0.01
  116. # Hell of a command isn't it.
  117. UI_elements.roundrect(layer, win,
  118. win.current["w"]-120,
  119. win.current["h"]-80,
  120. 40,
  121. 40,
  122. 10,
  123. button=do,
  124. icon="ok",
  125. tip=talk.text("checked"),
  126. url="asset_link")
  127. # Documentation entry
  128. def do():
  129. def after(win, var):
  130. pass
  131. studio_dialogs.help(win, "help", after, SEARCH=talk.text("documentation_link_assets"))
  132. UI_elements.roundrect(layer, win,
  133. 300,
  134. 50,
  135. 40,
  136. 40,
  137. 10,
  138. do,
  139. "question")
  140. # CANCEl
  141. if not win.current["linking_asset_data"]["process"]:
  142. def do():
  143. if not win.current["linking_asset_data"]["process"]:
  144. win.current["calls"][call]["var"] = False
  145. win.assets = {}
  146. UI_elements.roundrect(layer, win,
  147. win.current["w"]-80,
  148. win.current["h"]-80,
  149. 40,
  150. 40,
  151. 10,
  152. button=do,
  153. icon="cancel",
  154. tip=talk.text("cancel"),
  155. url="asset_link")
  156. # Short cut ESC
  157. if 65307 in win.current["keys"] and not win.textactive:
  158. do()
  159. # Now let's prepare the ground for the next part.
  160. # Before we do anything we need to make sure that there is an autolink.data
  161. # file in the extra folder of the shot. But since this is a system that is
  162. # designed to be very simple on the user side. There could not even be the
  163. # extra folder. So...
  164. try:
  165. os.mkdir(win.project+"/rnd"+win.cur+"/extra")
  166. except:
  167. pass
  168. # Next thing will be to see if there is a file called autolink.data. And if
  169. # yes read from it. We are going to load the data into the memory.
  170. # So if there is nothing yet loaded let's try loading it from file. Try
  171. # because there nothing quarantees that the file exists. If file does not
  172. # exists we gonna populate it with assets from the shot. As marked in the
  173. # scene the user wrote. But it's going to be a separate function since I
  174. # want to make a button to do it.
  175. if not win.current["linking_asset_data"]["assets"] and not win.current["linking_asset_data"]["read"]:
  176. win.current["linking_asset_data"]["read"] = True
  177. try:
  178. datafile = open(win.project+"/rnd"+win.cur+"/extra/autolink.data")
  179. datafile = datafile.read()
  180. datafile = datafile.split("\n")
  181. for line in datafile:
  182. if line.startswith("Link : "):
  183. asset = line[7:]
  184. win.current["linking_asset_data"]["assets"].append(asset)
  185. except:
  186. # So if it fails to load the file. Probably because it's not created
  187. # yet. Let's load from the story. What's so bad about it?
  188. link_from_script(win)
  189. # Now keep in mind that to help the script that runs inside blender all
  190. # assets have /dev/ in their folder. So to reach /ast/ blend file we will
  191. # need to parse the link. It's not hard just reminding it here.
  192. # IK everything is all over the place in the file but...
  193. # If there is a process. We should be able to see what it does.
  194. # Draw like a litte progress bar and stuff
  195. if win.current["linking_asset_data"]["process"]:
  196. # Basically during the execution of the linking. I want to read the
  197. # output and give the user some feedback of what is going on.
  198. currentline = win.current["linking_asset_data"]["process"].stdout.readline()[:-1]
  199. # In the bpy_do_linking.py file there is an instruction to write
  200. # out a fraction of a current operation into terminal. Which is
  201. # piped into the VCStudio and I can read it. So we can use it
  202. # to draw a progress bar.
  203. if currentline.startswith("FRACTION:"):
  204. try:
  205. fraction = float(currentline.split(":")[1])
  206. win.current["linking_asset_data"]["fraction"] = fraction
  207. except:
  208. pass
  209. if currentline == "FINISHED":
  210. win.current["linking_asset_data"]["process"] = False
  211. win.current["linking_asset_data"]["fraction"] = 1
  212. # The prgoress bar:
  213. if win.current["linking_asset_data"]["fraction"]:
  214. fraction = win.current["linking_asset_data"]["fraction"]
  215. UI_color.set(layer, win, "progress_background")
  216. UI_elements.roundrect(layer, win,
  217. 70,
  218. win.current["h"]-75,
  219. (win.current["w"]-220),
  220. 0,
  221. 7)
  222. UI_color.set(layer, win, "progress_active")
  223. UI_elements.roundrect(layer, win,
  224. 70,
  225. win.current["h"]-75,
  226. (win.current["w"]-220)*fraction,
  227. 0,
  228. 7)
  229. # This file will fuel of nightmares. Okay. So while developping it I realzed
  230. # one little problem. Overrides are kind a still buggy. Tho it's good to
  231. # have them for future releases. And most of rigs work just fine.
  232. # The thing that I want to do is to give the user options. Let's say their
  233. # rig is broken when trying Library- Overrides but it's okay if you do
  234. # legacy Proxy thing. So why not give the user the option of doing both.
  235. # And also sometimes you just want to link and decide later what you do with
  236. # your rig.
  237. # So I gonna make 3 icons. 3 modes so to speak. LINK, OVERRIDE, PROXY.
  238. linkoptions = {
  239. "link":"link_mode_link",
  240. "override":"link_mode_overrides", # Icon / Mode : Tooltip
  241. "proxy":"link_mode_proxy"
  242. }
  243. for num, mode in enumerate(linkoptions):
  244. if win.current["linking_asset_data"]["mode"] == mode:
  245. UI_color.set(layer, win, "progress_time")
  246. UI_elements.roundrect(layer, win,
  247. 50+(40*num),
  248. 50,
  249. 40,
  250. 40,
  251. 10)
  252. def do():
  253. win.current["linking_asset_data"]["mode"] = mode
  254. UI_elements.roundrect(layer, win,
  255. 50+(40*num),
  256. 50,
  257. 40,
  258. 40,
  259. 10,
  260. do,
  261. mode,
  262. tip=talk.text(linkoptions[mode]))
  263. # Let's add a button that adds assets based on what's written in the script.
  264. def do():
  265. link_from_script(win)
  266. UI_elements.roundrect(layer, win,
  267. 200,
  268. 50,
  269. 40,
  270. 40,
  271. 10,
  272. do,
  273. "scene_new",
  274. tip=talk.text("link_add_from_scene"))
  275. if not os.path.exists(win.project+win.current["linking_asset_data"]["selected"]+"/autolink.data")\
  276. or not os.path.exists(win.project+win.current["linking_asset_data"]["selected"].replace("/dev/","/ast/")+".blend"):
  277. def do():
  278. def after(win, var):
  279. print(var)
  280. studio_dialogs.asset_configure(win, "configuring_asset", after,
  281. win.current["linking_asset_data"]["selected"].replace("/dev",""))
  282. UI_elements.roundrect(layer, win,
  283. 250,
  284. 50,
  285. 40,
  286. 40,
  287. 10,
  288. button=do,
  289. icon="link_configure")
  290. # That's it. Now we are clipping and drawing the assets them selves.
  291. UI_elements.roundrect(layer, win,
  292. 50,
  293. 100,
  294. win.current["w"]-100,
  295. win.current["h"]-200,
  296. 10,
  297. fill=False)
  298. layer.clip()
  299. tileX = 70
  300. current_Y = 0
  301. if "asset_link" not in win.scroll:
  302. win.scroll["asset_link"] = 0
  303. #############################
  304. # Let's draw the list of assets that the user chose.
  305. for num, asset in enumerate(win.current["linking_asset_data"]["assets"]):
  306. cur, name = asset.split("/")[2], asset.split("/")[3]
  307. if int(current_Y + win.scroll["asset_link"] + 100) in range(0-100, win.current["h"]):
  308. # Making the layer
  309. nodesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 170, 200)
  310. node = cairo.Context(nodesurface)
  311. node.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
  312. UI_elements.roundrect(node, win,
  313. 0,
  314. 0,
  315. 170,
  316. 200,
  317. 10,
  318. fill=False)
  319. node.clip()
  320. # Background
  321. UI_color.set(node, win, "dark_overdrop")
  322. node.rectangle(0,0,170, 200)
  323. node.fill()
  324. # Banner
  325. UI_color.set(node, win, "node_asset")
  326. # Now the banner will be node_asset color if the asset is configured
  327. # but it could be not configured. Or not even have the asset blend.
  328. # so we need to quickly check both. And if any of them missing. Make
  329. # the banner a node_badfile color.
  330. warning = ""
  331. if not os.path.exists(win.project+asset+"/autolink.data")\
  332. or not os.path.exists(win.project+asset.replace("/dev/","/ast/")+".blend"):
  333. UI_color.set(node, win, "node_badfile")
  334. warning = "\n"+talk.text("link_asset_not_configured")
  335. # Let's do even cooler. And let's draw a thing arround the asset.
  336. node.set_line_width(10)
  337. UI_elements.roundrect(node, win,
  338. 0,
  339. 0,
  340. 170,
  341. 200,
  342. 10,
  343. fill=False)
  344. node.stroke()
  345. node.set_line_width(2)
  346. # Even cooler. Let's put a configuration button right into here
  347. node.rectangle(0,0,170, 20)
  348. node.fill()
  349. # Outputting the layer
  350. layer.set_source_surface(nodesurface,
  351. tileX-10,
  352. current_Y + win.scroll["asset_link"] + 120)
  353. layer.paint()
  354. # Previes image
  355. if os.path.exists(win.project+asset+"/renders/Preview.png"):
  356. UI_elements.image(layer, win, win.project+asset+"/renders/Preview.png",
  357. tileX,
  358. current_Y + win.scroll["asset_link"] + 140,
  359. 150,
  360. 150)
  361. elif os.path.exists(win.project+asset+"/renders/Preview.jpg"):
  362. UI_elements.image(layer, win, win.project+asset+"/renders/Preview.jpg",
  363. tileX,
  364. current_Y + win.scroll["asset_link"] + 140,
  365. 150,
  366. 150)
  367. else:
  368. UI_elements.image(layer, win, "settings/themes/"+win.settings["Theme"]+"/icons/"+cur+".png",
  369. tileX+55,
  370. current_Y + win.scroll["asset_link"] + 150+55,
  371. 150,
  372. 150)
  373. UI_color.set(layer, win, "text_normal")
  374. layer.set_font_size(12)
  375. layer.move_to(tileX,
  376. current_Y + win.scroll["asset_link"] + 135)
  377. layer.show_text(name[:22])
  378. # Let's make a selection of an asset so you could delete one. Or
  379. # do other stuff. Maybe.
  380. if win.current["linking_asset_data"]["selected"] == asset:
  381. layer.set_line_width(4)
  382. UI_color.set(layer, win, "progress_background")
  383. UI_elements.roundrect(layer, win,
  384. tileX-10,
  385. current_Y + win.scroll["asset_link"] + 120,
  386. 170,
  387. 200,
  388. 10,
  389. fill=False)
  390. layer.stroke()
  391. layer.set_line_width(2)
  392. # And the delete key.
  393. if 65535 in win.current["keys"]:
  394. try:
  395. win.current["linking_asset_data"]["assets"].remove(asset)
  396. except:
  397. pass
  398. win.current["keys"] = []
  399. # Button to activate it
  400. def do():
  401. win.current["linking_asset_data"]["selected"] = asset
  402. layer.set_line_width(4)
  403. UI_elements.roundrect(layer, win,
  404. tileX-10,
  405. current_Y + win.scroll["asset_link"] + 120,
  406. 170,
  407. 200,
  408. 10,
  409. button=do,
  410. tip=talk.text(cur)+": "+name+warning,
  411. fill=False,
  412. clip=[
  413. 50,
  414. 100,
  415. win.current["w"]-100,
  416. win.current["h"]-200
  417. ],
  418. url="asset_link")
  419. layer.stroke()
  420. layer.set_line_width(2)
  421. tileX += 200
  422. if tileX > win.current["w"]-220:
  423. tileX = 70
  424. current_Y += 230
  425. # Now here I want to put the add new one button. So you could add assets
  426. # to the stuff manually.
  427. def do():
  428. # Now this is going to be wild. Because I'm going to run a dialog with
  429. # in a dialog. And I hope that it's going to work.
  430. def after(win, var):
  431. # Funny that it's actually working.
  432. # Tho there is a little rub. If you press Cancel there is a bug that
  433. # makes nothing unusable. so...
  434. try:
  435. del win.current["calls"]["link_add_asset_select"]
  436. except:
  437. pass
  438. if var and "/dev"+var not in win.current["linking_asset_data"]["assets"]:
  439. win.current["linking_asset_data"]["assets"].append("/dev"+var)
  440. # So basically I want to add one only if there is not one already.
  441. # And then select it. So if the use gets which one is added or
  442. # whether it was already there.
  443. win.current["linking_asset_data"]["selected"] = "/dev"+var
  444. # And since it could allight with the add button. Let's clear the
  445. # click
  446. win.current["LMB"] = False
  447. win.previous["LMB"] = False
  448. studio_dialogs.asset_select(win, "link_add_asset_select", after)
  449. UI_elements.roundrect(layer, win,
  450. tileX-10,
  451. current_Y + win.scroll["asset_link"] + 120,
  452. 170,
  453. 200,
  454. 10,
  455. button=do,
  456. url="asset_link")
  457. UI_color.set(layer, win, "progress_background")
  458. UI_elements.roundrect(layer, win,
  459. tileX-10,
  460. current_Y + win.scroll["asset_link"] + 120,
  461. 170,
  462. 200,
  463. 10,
  464. fill=False)
  465. layer.stroke()
  466. UI_elements.image(layer, win,
  467. "settings/themes/"+win.settings["Theme"]+"/icons/asset_new.png",
  468. tileX+55,
  469. current_Y + win.scroll["asset_link"] + 200,
  470. 40, 40)
  471. #############################
  472. current_Y += 230
  473. UI_elements.scroll_area(layer, win, "asset_link",
  474. 50,
  475. 100,
  476. win.current["w"]-100,
  477. win.current["h"]-200,
  478. current_Y,
  479. bar=True,
  480. mmb=True,
  481. url="asset_link",
  482. strenght=130
  483. )
  484. return surface