studio_nodes.py 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603
  1. # THIS FILE IS A PART OF VCStudio
  2. # PYTHON 3
  3. import os
  4. import datetime
  5. # GTK module ( Graphical interface
  6. import gi
  7. gi.require_version('Gtk', '3.0')
  8. from gi.repository import Gtk
  9. import cairo
  10. # Own modules
  11. from settings import settings
  12. from settings import talk
  13. from settings import fileformats
  14. from settings import oscalls
  15. # Studio
  16. from studio import story
  17. from studio import analytics
  18. from studio import studio_dialogs
  19. from studio import checklist
  20. # UI modules
  21. from UI import UI_testing
  22. from UI import UI_color
  23. from UI import UI_elements
  24. from UI import UI_math
  25. def node_dot(layer, win, x, y, direction="in", entry="end"):
  26. # This function will draw a dot to which various nodes are connected. And
  27. # from which connections going off.
  28. # This is a very alpha version of the dot thing
  29. raw_entry = entry
  30. if type(entry) == list:
  31. entry = entry[0]+":"+entry[1]
  32. if entry.startswith("scene:") or entry in ["start", "end"]:
  33. UI_color.set(layer, win, "node_script")
  34. elif entry.startswith("blend:"):
  35. UI_color.set(layer, win, "node_blendfile")
  36. else:
  37. UI_color.set(layer, win, "node_imagefile")
  38. UI_elements.roundrect(layer, win,
  39. x,
  40. y,
  41. 0,
  42. 0,
  43. 6)
  44. if int(win.current["mx"]) in range(int(x), int(x+12))\
  45. and int(win.current["my"]) in range(int(y), int(y+12)):
  46. UI_color.set(layer, win, "text_normal")
  47. UI_elements.roundrect(layer, win,
  48. x,
  49. y,
  50. 0,
  51. 0,
  52. 6,
  53. fill=False)
  54. layer.stroke()
  55. win.current["cursor"] = win.cursors["arrow"]
  56. # Start drawing the line
  57. if not win.previous["LMB"] and win.current["LMB"] and direction != "in":
  58. # Out point
  59. win.current["tool"] = "connect"
  60. win.current["draw_dot"] = raw_entry
  61. elif not win.previous["LMB"] and win.current["LMB"] and direction == "in" and win.current["tool"] != "connect":
  62. # Take out of the in point
  63. try:
  64. found = False
  65. win.current["tool"] = "connect"
  66. for arrow in win.story["arrows"]:
  67. if raw_entry == arrow[1]:
  68. win.current["draw_dot"] = arrow[0]#.copy()
  69. found = arrow
  70. break
  71. if found:
  72. win.story["arrows"].remove(found)
  73. else:
  74. win.current["draw_dot"] = "end"
  75. except:
  76. pass
  77. # Refrashing analytics
  78. story.save(win.project, win.story)
  79. win.analytics = analytics.load(win.project)
  80. analytics.save(win.project, win.analytics)
  81. # Connecting the line
  82. if win.current["tool"] == "connect" and direction == "in" and not win.current["LMB"]:
  83. # Connecting the scenes together
  84. if raw_entry[0] not in ["file", "blend"] and win.current["draw_dot"][0] not in ["file", "blend"]:
  85. new_arrow = [
  86. win.current["draw_dot"],
  87. raw_entry
  88. ]
  89. if new_arrow not in win.story["arrows"]:
  90. win.story["arrows"].append(new_arrow)
  91. # Copying images to places
  92. elif raw_entry[0] == "asset" and win.current["draw_dot"][0] == "file":
  93. new = oscalls.copy_file(
  94. win,
  95. win.current["draw_dot"][1],
  96. "dev"+raw_entry[1])
  97. win.story["links"].append([
  98. "file", new, [
  99. win.current["mx"]-win.story["camera"][0]-75,
  100. win.current["my"]-win.story["camera"][1]-75
  101. ],
  102. "" # Parent
  103. ])
  104. # Now let's select and move the thing
  105. win.story["selected"] = [["file", len(win.story["links"])-1]]
  106. win.current["tool"] = "grab"
  107. win.current["LMB"] = [win.current["mx"], win.current["my"], True]
  108. elif raw_entry[0] == "blend" and win.current["draw_dot"][0] == "blend":
  109. new = oscalls.copy_file(
  110. win,
  111. win.current["draw_dot"][1],
  112. "dev"+raw_entry[1])
  113. win.story["links"].append([
  114. "file", new, [
  115. win.current["mx"]-win.story["camera"][0]-75,
  116. win.current["my"]-win.story["camera"][1]-75
  117. ],
  118. "" # Parent
  119. ])
  120. # Now let's select and move the thing
  121. win.story["selected"] = [["file", len(win.story["links"])-1]]
  122. win.current["tool"] = "grab"
  123. win.current["LMB"] = [win.current["mx"], win.current["my"], True]
  124. UI_math.filter_arrows(win)
  125. # Refrashing analytics
  126. story.save(win.project, win.story)
  127. win.analytics = analytics.load(win.project)
  128. analytics.save(win.project, win.analytics)
  129. if direction != "in":
  130. win.out_dots[entry] = [x+6, y+6]
  131. elif entry.startswith("scene:") or entry in ["start", "end"]:
  132. for arrow in win.story["arrows"]:
  133. if raw_entry == arrow[1]:
  134. fr = arrow[0]
  135. if type(fr) == list:
  136. fr = fr[0]+":"+fr[1]
  137. try:
  138. UI_color.set(layer, win, "node_script")
  139. layer.move_to(
  140. win.out_dots[fr][0],
  141. win.out_dots[fr][1]
  142. )
  143. layer.line_to(x+6, y+6)
  144. layer.stroke()
  145. except:
  146. pass
  147. elif entry.startswith("asset:"):
  148. for link in win.story["links"]:
  149. if link[0] == "file":
  150. link = link[1]
  151. fr = "file:"+link
  152. if entry[entry.find(":")+1:] in link:
  153. try:
  154. UI_color.set(layer, win, "node_imagefile")
  155. layer.move_to(
  156. win.out_dots[fr][0],
  157. win.out_dots[fr][1]
  158. )
  159. layer.line_to(x+6, y+6)
  160. layer.stroke()
  161. except:
  162. pass
  163. elif entry.startswith("blend:"):
  164. for link in win.story["links"]:
  165. if link[0] == "file":
  166. link = link[1]
  167. fr = "blend:"+link
  168. if entry[entry.find(":")+1:]+"/" in link or entry[entry.find(":")+1:]+"." in link:
  169. try:
  170. UI_color.set(layer, win, "node_blendfile")
  171. layer.move_to(
  172. win.out_dots[fr][0],
  173. win.out_dots[fr][1]
  174. )
  175. layer.line_to(x+6, y+6)
  176. layer.stroke()
  177. except:
  178. pass
  179. def start_node(outlayer, win, x, y, width, height):
  180. # This function will draw a start node in the top left corner of the story
  181. # editor. This is where the story begins.
  182. # Making the layer
  183. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  184. layer = cairo.Context(surface)
  185. # Clip
  186. UI_elements.roundrect(layer, win,
  187. 0,
  188. 0,
  189. width,
  190. height,
  191. 10,
  192. fill=False)
  193. layer.clip()
  194. # Background
  195. UI_color.set(layer, win, "dark_overdrop")
  196. layer.rectangle(0,0,width, height)
  197. layer.fill()
  198. # top banner
  199. UI_color.set(layer, win, "node_badfile")
  200. layer.rectangle(0,0,width, 20)
  201. layer.fill()
  202. # Text saying START
  203. UI_color.set(layer, win, "text_normal")
  204. layer.set_font_size(15)
  205. layer.move_to(width/2-len(talk.text("Start"))*9/2,15)
  206. layer.show_text(talk.text("Start"))
  207. # Outputting the layer
  208. outlayer.set_source_surface(surface, x, y)
  209. outlayer.paint()
  210. # Dot
  211. node_dot(outlayer, win, x+width-7, y+height-12, direction="out", entry="start")
  212. def end_node(outlayer, win, x, y, width, height):
  213. # This function will draw a end node in the bottom right corner of the story
  214. # editor. This is where the story ends.
  215. # Making the layer
  216. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  217. layer = cairo.Context(surface)
  218. # Clip
  219. UI_elements.roundrect(layer, win,
  220. 0,
  221. 0,
  222. width,
  223. height,
  224. 10,
  225. fill=False)
  226. layer.clip()
  227. # Background
  228. UI_color.set(layer, win, "dark_overdrop")
  229. layer.rectangle(0,0,width, height)
  230. layer.fill()
  231. # top banner
  232. UI_color.set(layer, win, "node_badfile")
  233. layer.rectangle(0,0,width, 20)
  234. layer.fill()
  235. # Text saying END
  236. UI_color.set(layer, win, "text_normal")
  237. layer.set_font_size(15)
  238. layer.move_to(width/2-len(talk.text("End"))*9/2,15)
  239. layer.show_text(talk.text("End"))
  240. # Outputting the layer
  241. outlayer.set_source_surface(surface, x, y)
  242. outlayer.paint()
  243. # Dot
  244. node_dot(outlayer, win, x-2, y+height-12)
  245. def scene_node(outlayer, win, x, y, width, height, name="Unknown", fraction=0.0, shots=[]):
  246. # This function will draw scene nodes.
  247. entry = ['scene', name]
  248. width = max(width,20)
  249. height = max(height,20)
  250. doparent = True
  251. if int(x) in range(int(0-width), int(win.current["w"]))\
  252. and int(y) in range(int(0-height), int(win.current["h"])):
  253. # Making the layer
  254. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  255. layer = cairo.Context(surface)
  256. layer.select_font_face("Monospace", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
  257. selected = False
  258. if win.url == "story_editor"\
  259. and int(win.current["mx"]) in range(50, int(win.current["w"]-50)) \
  260. and int(win.current["my"]) in range(50, int(win.current["h"]-30)):
  261. if win.current["LMB"] and entry in win.story["selected"]:
  262. if int(win.current["LMB"][0]) in range(int(x), int(x+width))\
  263. and int(win.current["LMB"][1]) in range(int(y), int(y+height))\
  264. and win.current["tool"] == "selection":
  265. win.current["tool"] = "grab"
  266. win.story["active"] = entry
  267. # Launching the item
  268. if int(win.current["mx"]) in range(int(x), int(x+width))\
  269. and int(win.current["my"]) in range(int(y), int(y+height)):
  270. win.current["cursor"] = win.cursors["hand"]
  271. if not win.current["LMB"] and win.previous["LMB"]\
  272. and entry in win.story["selected"]\
  273. and int(win.current["mx"]) == int(win.previous["LMB"][0])\
  274. and int(win.current["my"]) == int(win.previous["LMB"][1]):
  275. win.url = "script"
  276. win.cur = "/"+name
  277. win.current["tool"] = "selection"
  278. if win.current["LMB"] and win.current["tool"] == "selection":
  279. # If mouse over. But way more complex. Because we might select more then
  280. # 1 scene at ones.
  281. mx = win.current["mx"]
  282. my = win.current["my"]
  283. pmx = win.current["LMB"][0]
  284. pmy = win.current["LMB"][1]
  285. intersect = UI_math.rectangle_overlap(
  286. [mx, my, pmx-mx, pmy-my],
  287. [x,y,width, height])
  288. # Now let's make a higlight
  289. if intersect:
  290. selected = True
  291. elif win.previous["LMB"] and win.previous["tool"] == "selection":
  292. # If you released the mouse while selecting. Then do selecting. I guess.
  293. mx = win.previous["mx"]
  294. my = win.previous["my"]
  295. pmx = win.previous["LMB"][0]
  296. pmy = win.previous["LMB"][1]
  297. intersect = UI_math.rectangle_overlap(
  298. [mx, my, pmx-mx, pmy-my],
  299. [x,y,width, height])
  300. # Now let's make a selection
  301. if intersect:
  302. if entry not in win.story["selected"]:
  303. win.story["selected"].append(entry)
  304. if win.story["active"] not in win.story["selected"]:
  305. win.story["active"] = entry
  306. if entry in win.story["selected"]:
  307. selected = True
  308. if selected:
  309. if win.current["tool"] == "selection":
  310. UI_math.rectangle_surround(win, "selection",
  311. win.szone[0], win.szone[1],
  312. [x,y], [width, height]
  313. )
  314. # Now let's make the stratching thing do it's stratching. It's the
  315. # circle in the bottom, right corner of the selection.
  316. if win.current["tool"] == "scale" and win.current["LMB"] and win.previous["LMB"]:
  317. # let's get the points of the selection zone.
  318. zx = win.szone[0][0]
  319. zy = win.szone[0][1]
  320. zsx = win.previous["LMB"][0]
  321. zsy = win.previous["LMB"][1]
  322. if win.current["mx"] > zx and win.current["my"] > zy:
  323. # now let's get the motion fraction.
  324. mvx = (x - zx) / (zsx - zx)
  325. mvy = (y - zy) / (zsy - zy)
  326. svx = (win.current["mx"]- zx) / (win.previous["mx"]-zx)
  327. svy = (win.current["my"]- zy) / (win.previous["my"]-zy)
  328. # now let's apply these
  329. win.story["scenes"][name]["position"][0]\
  330. += (win.current["mx"] - win.previous["mx"])*mvx
  331. win.story["scenes"][name]["position"][1]\
  332. += (win.current["my"] - win.previous["my"])*mvy
  333. win.story["scenes"][name]["size"][0]\
  334. = max(win.story["scenes"][name]["size"][0] * svx, 40)
  335. win.story["scenes"][name]["size"][1]\
  336. = max(win.story["scenes"][name]["size"][1] * svy, 40)
  337. # Now let's do the simple grab tool.
  338. if win.current["tool"] == "grab":
  339. # If you press shift, You take things out of events
  340. if 65505 in win.current["keys"]:
  341. doparent = False
  342. try:
  343. if win.current["LMB"]:
  344. x += win.current["mx"] - win.current["LMB"][0]
  345. y += win.current["my"] - win.current["LMB"][1]
  346. elif win.previous["LMB"]:
  347. win.story["scenes"][name]["position"][0]\
  348. += win.previous["mx"] - win.previous["LMB"][0]
  349. win.story["scenes"][name]["position"][1]\
  350. += win.previous["my"] - win.previous["LMB"][1]
  351. x += win.previous["mx"] - win.previous["LMB"][0]
  352. y += win.previous["my"] - win.previous["LMB"][1]
  353. # Parent removing / assigning
  354. if not doparent:
  355. win.story["scenes"][name]["parent"] = ""
  356. elif not win.story["scenes"][name]["parent"]:
  357. for event in win.story["events"]:
  358. mx, my = win.story["events"][event]["position"]
  359. pmx, pmy = win.story["events"][event]["size"]
  360. if UI_math.rectangle_overlap(
  361. [mx, my, pmx, pmy],
  362. [x,y,width, height]):
  363. win.story["scenes"][name]["parent"] = event
  364. break
  365. elif win.current["tool"] != "connect":
  366. win.current["tool"] = "selection"
  367. except:
  368. raise()
  369. # The selected outline
  370. UI_color.set(outlayer, win, "progress_background")
  371. if win.story["active"] == entry:
  372. UI_color.set(outlayer, win, "text_normal")
  373. outlayer.set_line_width(4)
  374. UI_elements.roundrect(outlayer, win,
  375. x,
  376. y,
  377. width,
  378. height,
  379. 10,
  380. fill=False)
  381. outlayer.stroke()
  382. outlayer.set_line_width(2)
  383. # Clip
  384. UI_elements.roundrect(layer, win,
  385. 0,
  386. 0,
  387. width,
  388. height,
  389. 10,
  390. fill=False)
  391. layer.clip()
  392. # Tooltip
  393. if int(win.current["mx"]) in range(int(x), int(x+width))\
  394. and int(win.current["my"]) in range(int(y), int(y+height)):
  395. UI_elements.tooltip(win, name)
  396. # Background
  397. UI_color.set(layer, win, "dark_overdrop")
  398. layer.rectangle(0,0,width, height)
  399. layer.fill()
  400. # top banner
  401. UI_color.set(layer, win, "node_script")
  402. layer.rectangle(0,0,width, 20)
  403. layer.fill()
  404. # Text saying The name of the scene
  405. UI_color.set(layer, win, "text_normal")
  406. layer.set_font_size(15)
  407. layer.move_to(15,15)
  408. layer.show_text(name)
  409. text_height = 10
  410. # Fraction
  411. if fraction:
  412. text_height = 40
  413. UI_color.set(layer, win, "progress_background")
  414. UI_elements.roundrect(layer, win,
  415. 10,
  416. height-20,
  417. width-20,
  418. 0,
  419. 5)
  420. UI_color.set(layer, win, "progress_active")
  421. UI_elements.roundrect(layer, win,
  422. 10,
  423. height-20,
  424. (width-20)*fraction,
  425. 0,
  426. 5)
  427. # A bit of the scene itself if the size allows.
  428. if width > 100 and height > text_height:
  429. # Very simple text from the scene, nothing fancy
  430. alltext = ""
  431. for i in shots:
  432. for b in i[-1]:
  433. if b[0] == "frase":
  434. alltext = alltext + b[1][-1]+": "
  435. alltext = alltext + b[-1]
  436. alltext = alltext.replace("\n", " \n ")
  437. # Rendering it.
  438. fx = 15
  439. fy = 50
  440. for word in alltext.split(" "):
  441. if word == "\n" or len(word)*9 + fx > width:
  442. fy = fy + 20
  443. fx = 15
  444. if fy > height - text_height:
  445. break
  446. if word != "\n":
  447. UI_color.set(layer, win, "text_normal")
  448. layer.set_font_size(15)
  449. layer.move_to(fx,fy)
  450. layer.show_text(word)
  451. fx = fx + len(word)*9 + 10
  452. # Outputting the layer
  453. outlayer.set_source_surface(surface, x, y)
  454. outlayer.paint()
  455. # In case there is a parent event in the scene.
  456. if win.story["scenes"][name]["parent"] and doparent:
  457. parent = win.story["scenes"][name]["parent"]
  458. UI_math.rectangle_surround(win, parent,
  459. win.story["events"][parent]["position"],
  460. win.story["events"][parent]["size"],
  461. [x,y], [width, height]
  462. )
  463. # Dots
  464. node_dot(outlayer, win, x-5, y+25, entry=entry)
  465. node_dot(outlayer, win, x+width-7, y+25, direction="out",entry=entry)
  466. def event_node(outlayer, win, x, y, width, height, name="Unknown"):
  467. # This function draws events.
  468. x = x - 20
  469. y = y - 20
  470. width = width + 40
  471. height = height + 40
  472. entry = ['event', name]
  473. # Making sure the size is alright
  474. if width < 100:
  475. width = 100
  476. if height < 100:
  477. height = 100
  478. # Making the layer
  479. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  480. layer = cairo.Context(surface)
  481. # Clip
  482. UI_elements.roundrect(layer, win,
  483. 0,
  484. 0,
  485. width,
  486. height,
  487. 10,
  488. fill=False)
  489. layer.clip()
  490. # Background
  491. UI_color.set(layer, win, "dark_overdrop")
  492. layer.rectangle(0,0,width, height)
  493. layer.fill()
  494. # Here should be the event name thing. But I thought that it wasn't
  495. # important for a simple framing thing.
  496. # Outputting the layer
  497. outlayer.set_source_surface(surface, x, y)
  498. outlayer.paint()
  499. def link_node(outlayer, win, x, y, width=150, height=150, name="", num=0, linktype="file"):
  500. filetype = ""
  501. # This node will output links to files.
  502. if linktype == "asset":
  503. width += 50
  504. height += 80
  505. else:
  506. # Let's find out what type of file is it.
  507. for f in fileformats.images:
  508. if name.endswith(f):
  509. filetype = "image"
  510. if not filetype:
  511. for f in fileformats.videos:
  512. if name.endswith(f):
  513. filetype = "video"
  514. doparent = True
  515. if int(x) in range(int(0-width), int(win.current["w"]))\
  516. and int(y) in range(int(0-height), int(win.current["h"])):
  517. # Making the layer
  518. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  519. layer = cairo.Context(surface)
  520. entry = [linktype, num]
  521. selected = False
  522. if win.url == "story_editor"\
  523. and int(win.current["mx"]) in range(50, int(win.current["w"]-50)) \
  524. and int(win.current["my"]) in range(50, int(win.current["h"]-30)):
  525. if win.current["LMB"] and entry in win.story["selected"]:
  526. if int(win.current["LMB"][0]) in range(int(x), int(x+width))\
  527. and int(win.current["LMB"][1]) in range(int(y), int(y+height))\
  528. and win.current["tool"] == "selection":
  529. win.current["tool"] = "grab"
  530. win.story["active"] = entry
  531. # Launching the item
  532. if int(win.current["mx"]) in range(int(x), int(x+width))\
  533. and int(win.current["my"]) in range(int(y), int(y+height)):
  534. win.current["cursor"] = win.cursors["hand"]
  535. if not win.current["LMB"] and win.previous["LMB"]\
  536. and entry in win.story["selected"]\
  537. and int(win.current["mx"]) == int(win.previous["LMB"][0])\
  538. and int(win.current["my"]) == int(win.previous["LMB"][1]):
  539. if linktype == "file":
  540. oscalls.file_open(win, name)
  541. elif int(win.current["mx"]) > x+40: # This is a hack. But whatever.
  542. win.url = "assets"
  543. win.cur = name
  544. win.current["tool"] = "selection"
  545. win.previous["LMB"] = False
  546. if win.current["LMB"] and win.current["tool"] == "selection":
  547. # If mouse over. But way more complex. Because we might select more then
  548. # 1 scene at ones.
  549. mx = win.current["mx"]
  550. my = win.current["my"]
  551. pmx = win.current["LMB"][0]
  552. pmy = win.current["LMB"][1]
  553. intersect = UI_math.rectangle_overlap(
  554. [mx, my, pmx-mx, pmy-my],
  555. [x,y,width, height])
  556. # Now let's make a higlight
  557. if intersect:
  558. selected = True
  559. elif win.previous["LMB"] and win.previous["tool"] == "selection":
  560. # If you released the mouse while selecting. Then do selecting. I guess.
  561. mx = win.previous["mx"]
  562. my = win.previous["my"]
  563. pmx = win.previous["LMB"][0]
  564. pmy = win.previous["LMB"][1]
  565. intersect = UI_math.rectangle_overlap(
  566. [mx, my, pmx-mx, pmy-my],
  567. [x,y,width, height])
  568. # Now let's make a selection
  569. if intersect:
  570. if entry not in win.story["selected"]:
  571. win.story["selected"].append(entry)
  572. # Making it active.
  573. if win.story["active"] not in win.story["selected"]:
  574. win.story["active"] = entry
  575. if entry in win.story["selected"]:
  576. selected = True
  577. if selected:
  578. if win.current["tool"] == "selection":
  579. UI_math.rectangle_surround(win, "selection",
  580. win.szone[0], win.szone[1],
  581. [x,y], [width, height]
  582. )
  583. # Now let's make the stratching thing do it's stratching. It's the
  584. # circle in the bottom, right corner of the selection.
  585. if win.current["tool"] == "scale" and win.current["LMB"] and win.previous["LMB"]:
  586. # let's get the points of the selection zone.
  587. zx = win.szone[0][0]
  588. zy = win.szone[0][1]
  589. zsx = win.previous["LMB"][0]
  590. zsy = win.previous["LMB"][1]
  591. if win.current["mx"] > zx and win.current["my"] > zy:
  592. # now let's get the motion fraction.
  593. mvx = (x - zx) / (zsx - zx)
  594. mvy = (y - zy) / (zsy - zy)
  595. # now let's apply these
  596. win.story["links"][num][2][0]\
  597. += (win.current["mx"] - win.previous["mx"])*mvx
  598. win.story["links"][num][2][1]\
  599. += (win.current["my"] - win.previous["my"])*mvy
  600. if win.current["tool"] == "grab":
  601. # If you press shift, You take things out of events
  602. if 65505 in win.current["keys"]:
  603. doparent = False
  604. try:
  605. if win.current["LMB"]:
  606. x += win.current["mx"] - win.current["LMB"][0]
  607. y += win.current["my"] - win.current["LMB"][1]
  608. elif win.previous["LMB"]:
  609. win.story["links"][num][2][0]\
  610. += win.previous["mx"] - win.previous["LMB"][0]
  611. win.story["links"][num][2][1]\
  612. += win.previous["my"] - win.previous["LMB"][1]
  613. x += win.previous["mx"] - win.previous["LMB"][0]
  614. y += win.previous["my"] - win.previous["LMB"][1]
  615. # Parent removing / assigning
  616. if not doparent:
  617. win.story["links"][num][3] = ""
  618. elif not win.story["links"][num][3]:
  619. for event in win.story["events"]:
  620. mx, my = win.story["events"][event]["position"]
  621. pmx, pmy = win.story["events"][event]["size"]
  622. if UI_math.rectangle_overlap(
  623. [mx, my, pmx, pmy],
  624. [x,y,width, height]):
  625. win.story["links"][num][3] = event
  626. break
  627. elif win.current["tool"] != "connect":
  628. win.current["tool"] = "selection"
  629. except:
  630. raise()
  631. # The selected outline
  632. UI_color.set(outlayer, win, "progress_background")
  633. if win.story["active"] == entry:
  634. UI_color.set(outlayer, win, "text_normal")
  635. outlayer.set_line_width(4)
  636. UI_elements.roundrect(outlayer, win,
  637. x,
  638. y,
  639. width,
  640. height,
  641. 10,
  642. fill=False)
  643. outlayer.stroke()
  644. outlayer.set_line_width(2)
  645. # Clip
  646. UI_elements.roundrect(layer, win,
  647. 0,
  648. 0,
  649. width,
  650. height,
  651. 10,
  652. fill=False)
  653. layer.clip()
  654. # Background
  655. UI_color.set(layer, win, "dark_overdrop")
  656. layer.rectangle(0,0,width, height)
  657. layer.fill()
  658. if linktype == "asset":
  659. assettype = name[:name.rfind("/")].replace("/", "")
  660. if os.path.exists(win.project+"/dev"+name+"/renders/Preview.png"):
  661. UI_elements.image(layer, win,
  662. win.project+"/dev"+name+"/renders/Preview.png",
  663. 0, 0, width, height, cell="story_asset_previews")
  664. elif os.path.exists(win.project+"/dev"+name+"/renders/Preview.jpg"):
  665. UI_elements.image(layer, win,
  666. win.project+"/dev"+name+"/renders/Preview.jpg",
  667. 0, 0, width, height, cell="story_asset_previews")
  668. else:
  669. UI_elements.image(layer, win,
  670. "settings/themes/"+win.settings["Theme"]+"/icons/"+assettype+".png",
  671. width/2-20, height/2-20, 40, 40)
  672. # Here I want to add some buttons for accessing the various folder of
  673. # the asset. And also to link stuff into those folders using nodes.
  674. if int(win.current["mx"]) in range(int(x), int(x+width))\
  675. and int(win.current["my"]) in range(int(y), int(y+height)):
  676. # Darkening thing.
  677. UI_color.set(layer, win, "dark_overdrop")
  678. layer.rectangle(0,0,50, height)
  679. layer.fill()
  680. def after(win, var):
  681. if var:
  682. win.story["links"].append([
  683. "file", var, [
  684. win.current["mx"]-win.story["camera"][0]-75,
  685. win.current["my"]-win.story["camera"][1]-75
  686. ],
  687. ""
  688. ])
  689. # Now let's select and move the thing
  690. win.story["selected"] = [["file", len(win.story["links"])-1]]
  691. win.current["tool"] = "grab"
  692. win.current["LMB"] = [win.current["mx"], win.current["my"], True]
  693. # Blendfiles
  694. def do():
  695. studio_dialogs.file_select(win, name+"_blends", after, force=True,
  696. IMAGE=False, BLEND=True, VIDEO=False, FILE=False, CHR=True, VEH=True,
  697. LOC=True, OBJ=True, RND=False, FOLDER=False, SEARCH=name)
  698. UI_elements.roundrect(layer, win,
  699. 5,
  700. 25,
  701. 40,
  702. 40,
  703. 10,
  704. do,
  705. "blender",
  706. talk.text("blend_files_folder"),
  707. url="story_editor",
  708. offset=[x,y])
  709. # References
  710. def do():
  711. studio_dialogs.file_select(win, name+"_reference", after, force=True,
  712. IMAGE=True, BLEND=False, VIDEO=True, FILE=False, CHR=True, VEH=True,
  713. LOC=True, OBJ=True, RND=False, FOLDER=False, SEARCH=name+"/reference")
  714. UI_elements.roundrect(layer, win,
  715. 5,
  716. 75,
  717. 40,
  718. 40,
  719. 10,
  720. do,
  721. "idea",
  722. talk.text("reference_folder"),
  723. url="story_editor",
  724. offset=[x,y])
  725. # Textures
  726. def do():
  727. studio_dialogs.file_select(win, name+"_textures", after, force=True,
  728. IMAGE=True, BLEND=False, VIDEO=True, FILE=False, CHR=True, VEH=True,
  729. LOC=True, OBJ=True, RND=False, FOLDER=False, SEARCH=name+"/tex")
  730. UI_elements.roundrect(layer, win,
  731. 5,
  732. 125,
  733. 40,
  734. 40,
  735. 10,
  736. do,
  737. "texture",
  738. talk.text("tex_folder"),
  739. url="story_editor",
  740. offset=[x,y])
  741. # Renders
  742. def do():
  743. studio_dialogs.file_select(win, name+"_renders", after, force=True,
  744. IMAGE=True, BLEND=False, VIDEO=True, FILE=False, CHR=True, VEH=True,
  745. LOC=True, OBJ=True, RND=False, FOLDER=False, SEARCH=name+"/renders")
  746. UI_elements.roundrect(layer, win,
  747. 5,
  748. 175,
  749. 40,
  750. 40,
  751. 10,
  752. do,
  753. "render",
  754. talk.text("renders_folder"),
  755. url="story_editor",
  756. offset=[x,y])
  757. # Fraction
  758. fraction = story.get_asset_data(win, name)["fraction"]
  759. UI_color.set(layer, win, "progress_background")
  760. UI_elements.roundrect(layer, win,
  761. 65,
  762. height-20,
  763. width-85,
  764. 0,
  765. 5)
  766. UI_color.set(layer, win, "progress_active")
  767. UI_elements.roundrect(layer, win,
  768. 65,
  769. height-20,
  770. (width-85)*fraction,
  771. 0,
  772. 5)
  773. else:
  774. # Fraction
  775. fraction = story.get_asset_data(win, name)["fraction"]
  776. UI_color.set(layer, win, "progress_background")
  777. UI_elements.roundrect(layer, win,
  778. 10,
  779. height-20,
  780. width-20,
  781. 0,
  782. 5)
  783. UI_color.set(layer, win, "progress_active")
  784. UI_elements.roundrect(layer, win,
  785. 10,
  786. height-20,
  787. (width-20)*fraction,
  788. 0,
  789. 5)
  790. UI_color.set(layer, win, "node_asset")
  791. # Tooltip
  792. if int(win.current["mx"]) in range(int(x+40), int(x+width))\
  793. and int(win.current["my"]) in range(int(y), int(y+height)):
  794. UI_elements.tooltip(win, name)
  795. else:
  796. if os.path.exists(win.project+"/"+name):
  797. UI_elements.image(layer, win,
  798. win.project+"/"+name,
  799. 0, 0, width, height)
  800. else:
  801. UI_elements.image(layer, win,
  802. name,
  803. 0, 0, width, height)
  804. if name.endswith(".progress"):
  805. fraction = checklist.get_fraction(win, name)
  806. UI_color.set(layer, win, "progress_background")
  807. UI_elements.roundrect(layer, win,
  808. 10,
  809. height-20,
  810. width-20,
  811. 0,
  812. 5)
  813. UI_color.set(layer, win, "progress_active")
  814. UI_elements.roundrect(layer, win,
  815. 10,
  816. height-20,
  817. (width-20)*fraction,
  818. 0,
  819. 5)
  820. if name.endswith(".blend"):
  821. UI_color.set(layer, win, "node_blendfile")
  822. if "ast/" in name:
  823. UI_color.set(layer, win, "node_asset")
  824. elif filetype == "image":
  825. UI_color.set(layer, win, "node_imagefile")
  826. elif filetype == "video":
  827. UI_color.set(layer, win, "node_videofile")
  828. else:
  829. UI_color.set(layer, win, "node_script")
  830. # Tooltip
  831. if int(win.current["mx"]) in range(int(x), int(x+width))\
  832. and int(win.current["my"]) in range(int(y), int(y+height)):
  833. UI_elements.tooltip(win, name)
  834. # top banner
  835. layer.rectangle(0,0,width, 20)
  836. layer.fill()
  837. # Text saying The name of the event
  838. UI_color.set(layer, win, "text_normal")
  839. layer.set_font_size(15)
  840. layer.move_to(15,15)
  841. layer.show_text(name[name.rfind("/")+1:])
  842. # Outputting the layer
  843. outlayer.set_source_surface(surface, x, y)
  844. outlayer.paint()
  845. if linktype == "asset":
  846. node_dot(outlayer, win, x-6, y+25+14, entry=["blend", name])
  847. node_dot(outlayer, win, x-6, y+75+14, entry=["asset", name+"/reference"])
  848. node_dot(outlayer, win, x-6, y+125+14, entry=["asset", name+"/tex"])
  849. node_dot(outlayer, win, x-6, y+175+14, entry=["asset", name+"/renders"])
  850. else:
  851. if name.endswith(".blend"):
  852. node_dot(outlayer, win, x+width-6, y+120, entry=["blend", name], direction="out")
  853. elif filetype in ["image", "video"]:
  854. node_dot(outlayer, win, x+width-6, y+120, entry=["file", name], direction="out")
  855. else: # If not in the frame
  856. if linktype == "file":
  857. try:
  858. if name.endswith(".blend"):
  859. del win.out_dots["blend:"+name]
  860. else:
  861. del win.out_dots["file:"+name]
  862. except:
  863. pass
  864. # In case there is a parent event in the scene.
  865. if win.story["links"][num][3] and doparent:
  866. parent = win.story["links"][num][3]
  867. UI_math.rectangle_surround(win, parent,
  868. win.story["events"][parent]["position"],
  869. win.story["events"][parent]["size"],
  870. [x,y], [width, height]
  871. )
  872. def marker(outlayer, win, name ,x, y ):
  873. # This function will draw markers to the screen. They are like shortcuts in
  874. # a story editor space.
  875. if int(x) in range(int(60), int(win.current["w"]-100))\
  876. and int(y) in range(int(60), int(win.current["h"]-80)):
  877. width = 200
  878. height = 40
  879. inscreen = True
  880. else:
  881. x = min(max(x, 60), win.current["w"]-100)
  882. y = min(max(y, 60), win.current["h"]-80)
  883. width = 40
  884. height = 40
  885. inscreen = False
  886. # Making the layer
  887. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  888. layer = cairo.Context(surface)
  889. entry = ["marker", name]
  890. if inscreen:
  891. doparent = True
  892. selected = False
  893. if win.url == "story_editor"\
  894. and int(win.current["mx"]) in range(50, int(win.current["w"]-50)) \
  895. and int(win.current["my"]) in range(50, int(win.current["h"]-30)):
  896. if win.current["LMB"] and entry in win.story["selected"]:
  897. if int(win.current["LMB"][0]) in range(int(x), int(x+40))\
  898. and int(win.current["LMB"][1]) in range(int(y), int(y+height))\
  899. and win.current["tool"] == "selection":
  900. win.current["tool"] = "grab"
  901. win.story["active"] = entry
  902. if win.current["LMB"] and win.current["tool"] == "selection":
  903. # If mouse over. But way more complex. Because we might select more then
  904. # 1 scene at ones.
  905. mx = win.current["mx"]
  906. my = win.current["my"]
  907. pmx = win.current["LMB"][0]
  908. pmy = win.current["LMB"][1]
  909. intersect = UI_math.rectangle_overlap(
  910. [mx, my, pmx-mx, pmy-my],
  911. [x,y,width, height])
  912. # Now let's make a higlight
  913. if intersect:
  914. selected = True
  915. elif win.previous["LMB"] and win.previous["tool"] == "selection":
  916. # If you released the mouse while selecting. Then do selecting. I guess.
  917. mx = win.previous["mx"]
  918. my = win.previous["my"]
  919. pmx = win.previous["LMB"][0]
  920. pmy = win.previous["LMB"][1]
  921. intersect = UI_math.rectangle_overlap(
  922. [mx, my, pmx-mx, pmy-my],
  923. [x,y,width, height])
  924. # Now let's make a selection
  925. if intersect:
  926. if entry not in win.story["selected"]:
  927. win.story["selected"].append(entry)
  928. # Making it active.
  929. if win.story["active"] not in win.story["selected"]:
  930. win.story["active"] = entry
  931. if entry in win.story["selected"]:
  932. selected = True
  933. if selected:
  934. if win.current["tool"] == "selection":
  935. UI_math.rectangle_surround(win, "selection",
  936. win.szone[0], win.szone[1],
  937. [x,y], [width, height]
  938. )
  939. # Now let's make the stratching thing do it's stratching. It's the
  940. # circle in the bottom, right corner of the selection.
  941. if win.current["tool"] == "scale" and win.current["LMB"] and win.previous["LMB"]:
  942. # let's get the points of the selection zone.
  943. zx = win.szone[0][0]
  944. zy = win.szone[0][1]
  945. zsx = win.previous["LMB"][0]
  946. zsy = win.previous["LMB"][1]
  947. if win.current["mx"] > zx and win.current["my"] > zy:
  948. # now let's get the motion fraction.
  949. mvx = (x - zx) / (zsx - zx)
  950. mvy = (y - zy) / (zsy - zy)
  951. # now let's apply these
  952. win.story["markers"][name][0]\
  953. += (win.current["mx"] - win.previous["mx"])*mvx
  954. win.story["markers"][name][1]\
  955. += (win.current["my"] - win.previous["my"])*mvy
  956. if win.current["tool"] == "grab":
  957. # If you press shift, You take things out of events
  958. if 65505 in win.current["keys"]:
  959. doparent = False
  960. try:
  961. if win.current["LMB"]:
  962. x += win.current["mx"] - win.current["LMB"][0]
  963. y += win.current["my"] - win.current["LMB"][1]
  964. elif win.previous["LMB"]:
  965. win.story["markers"][name][0]\
  966. += win.previous["mx"] - win.previous["LMB"][0]
  967. win.story["markers"][name][1]\
  968. += win.previous["my"] - win.previous["LMB"][1]
  969. x += win.previous["mx"] - win.previous["LMB"][0]
  970. y += win.previous["my"] - win.previous["LMB"][1]
  971. # Parent removing / assigning
  972. if not doparent:
  973. win.story["markers"][name][2] = ""
  974. elif not win.story["markers"][name][2] :
  975. for event in win.story["events"]:
  976. mx, my = win.story["events"][event]["position"]
  977. pmx, pmy = win.story["events"][event]["size"]
  978. if UI_math.rectangle_overlap(
  979. [mx, my, pmx, pmy],
  980. [x,y,width, height]):
  981. win.story["markers"][name][2] = event
  982. print(event)
  983. break
  984. elif win.current["tool"] != "connect":
  985. win.current["tool"] = "selection"
  986. except:
  987. raise()
  988. # The selected outline
  989. UI_color.set(outlayer, win, "progress_background")
  990. if win.story["active"] == entry:
  991. UI_color.set(outlayer, win, "text_normal")
  992. outlayer.set_line_width(4)
  993. UI_elements.roundrect(outlayer, win,
  994. x,
  995. y,
  996. width,
  997. height,
  998. 10,
  999. fill=False)
  1000. outlayer.stroke()
  1001. outlayer.set_line_width(2)
  1002. # Clip
  1003. UI_elements.roundrect(layer, win,
  1004. 0,
  1005. 0,
  1006. width,
  1007. height,
  1008. 10,
  1009. fill=False)
  1010. layer.clip()
  1011. # Background
  1012. UI_color.set(layer, win, "dark_overdrop")
  1013. layer.rectangle(0,0,width, height)
  1014. layer.fill()
  1015. # Pin Icon
  1016. UI_elements.image(layer, win,
  1017. "settings/themes/"+win.settings["Theme"]+"/icons/pin.png",
  1018. 0, 0, 40, 40)
  1019. # Outputting the layer
  1020. outlayer.set_source_surface(surface, x, y)
  1021. outlayer.paint()
  1022. if inscreen:
  1023. # Text editor for the marker's name.
  1024. UI_elements.text(outlayer, win, name+"_marker",
  1025. x+40,
  1026. y,
  1027. width-40,
  1028. 40,
  1029. set_text=name)
  1030. if name != win.text[name+"_marker"]["text"] and win.text[name+"_marker"]["text"]:
  1031. def do():
  1032. win.story["selected"] = []
  1033. win.story["markers"][win.text[name+"_marker"]["text"]] = [
  1034. x - win.story["camera"][0],
  1035. y - win.story["camera"][1],
  1036. win.story["markers"][name][2]
  1037. ]
  1038. win.textactive = ""
  1039. del win.story["markers"][name]
  1040. UI_elements.roundrect(outlayer, win,
  1041. x+width-40,
  1042. y,
  1043. 40,
  1044. 40,
  1045. 10,
  1046. button=do,
  1047. icon="ok",
  1048. tip=talk.text("checked"))
  1049. if 65293 in win.current["keys"]:
  1050. do()
  1051. # In case there is a parent event in the scene.
  1052. if win.story["markers"][name][2] and doparent:
  1053. parent = win.story["markers"][name][2]
  1054. UI_math.rectangle_surround(win, parent,
  1055. win.story["events"][parent]["position"],
  1056. win.story["events"][parent]["size"],
  1057. [x,y], [width, height]
  1058. )
  1059. else:
  1060. if win.textactive == name+"_marker":
  1061. win.textactive = ""
  1062. def do():
  1063. nex = 0-win.story["markers"][name][0] + win.current["w"]/2
  1064. ney = 0-win.story["markers"][name][1] + win.current["h"]/2
  1065. UI_elements.animate("cameraX", win, win.story["camera"][0],nex, time=20, force=True)
  1066. UI_elements.animate("cameraY", win, win.story["camera"][1],ney, time=20, force=True)
  1067. UI_elements.roundrect(outlayer, win,
  1068. x+width-40,
  1069. y,
  1070. 40,
  1071. 40,
  1072. 10,
  1073. button=do,
  1074. tip=name,
  1075. fill=False)
  1076. outlayer.stroke()
  1077. def user(outlayer, win, name ,x, y, user):
  1078. # This function will draw users from multiuser to the screen.
  1079. if int(x) in range(int(60), int(win.current["w"]-100))\
  1080. and int(y) in range(int(60), int(win.current["h"]-80)):
  1081. width = 200
  1082. height = 40
  1083. inscreen = True
  1084. else:
  1085. x = min(max(x, 60), win.current["w"]-100)
  1086. y = min(max(y, 60), win.current["h"]-80)
  1087. width = 40
  1088. height = 40
  1089. inscreen = False
  1090. # Making the layer
  1091. surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height))
  1092. layer = cairo.Context(surface)
  1093. # Clip
  1094. UI_elements.roundrect(layer, win,
  1095. 0,
  1096. 0,
  1097. width,
  1098. height,
  1099. 10,
  1100. fill=False)
  1101. layer.clip()
  1102. # Background
  1103. UI_color.set(layer, win, "dark_overdrop")
  1104. layer.rectangle(0,0,width, height)
  1105. layer.fill()
  1106. # Pin Icon
  1107. UI_elements.image(layer, win,
  1108. "settings/themes/"+win.settings["Theme"]+"/icons/user.png",
  1109. 0, 0, 40, 40)
  1110. # Outputting the layer
  1111. outlayer.set_source_surface(surface, x, y)
  1112. outlayer.paint()
  1113. if inscreen:
  1114. # Text editor for the marker's name.
  1115. UI_elements.text(outlayer, win, name+"_user",
  1116. x+40,
  1117. y,
  1118. width-40,
  1119. 40,
  1120. set_text=name,
  1121. editable=False)
  1122. else:
  1123. # Going to the other user with 1 click
  1124. def do():
  1125. nex = win.multiuser["users"][user]["camera"][0]
  1126. ney = win.multiuser["users"][user]["camera"][1]
  1127. UI_elements.animate("cameraX", win, win.story["camera"][0],nex, time=20, force=True)
  1128. UI_elements.animate("cameraY", win, win.story["camera"][1],ney, time=20, force=True)
  1129. UI_elements.roundrect(outlayer, win,
  1130. x+width-40,
  1131. y,
  1132. 40,
  1133. 40,
  1134. 10,
  1135. button=do,
  1136. tip=name,
  1137. fill=False)
  1138. outlayer.stroke()