ui.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. #####################################################################
  2. # #
  3. # THIS IS A SOURCE CODE FILE FROM A PROGRAM TO INTERACT WITH THE #
  4. # LBRY PROTOCOL ( lbry.com ). IT WILL USE THE LBRY SDK ( lbrynet ) #
  5. # FROM THEIR REPOSITORY ( https://github.com/lbryio/lbry-sdk ) #
  6. # WHICH I GONNA PRESENT TO YOU AS A BINARY. SINCE I DID NOT DEVELOP #
  7. # IT AND I'M LAZY TO INTEGRATE IN A MORE SMART WAY. THE SOURCE CODE #
  8. # OF THE SDK IS AVAILABLE IN THE REPOSITORY MENTIONED ABOVE. #
  9. # #
  10. # ALL THE CODE IN THIS REPOSITORY INCLUDING THIS FILE IS #
  11. # (C) J.Y.Amihud and Other Contributors 2021. EXCEPT THE LBRY SDK. #
  12. # YOU CAN USE THIS FILE AND ANY OTHER FILE IN THIS REPOSITORY UNDER #
  13. # THE TERMS OF GNU GENERAL PUBLIC LICENSE VERSION 3 OR ANY LATER #
  14. # VERSION. TO FIND THE FULL TEXT OF THE LICENSE GO TO THE GNU.ORG #
  15. # WEBSITE AT ( https://www.gnu.org/licenses/gpl-3.0.html ). #
  16. # #
  17. # THE LBRY SDK IS UNFORTUNATELY UNDER THE MIT LICENSE. IF YOU ARE #
  18. # NOT INTENDING TO USE MY CODE AND JUST THE SDK. YOU CAN FIND IT ON #
  19. # THEIR OFFICIAL REPOSITORY ABOVE. THEIR LICENSE CHOICE DOES NOT #
  20. # SPREAD ONTO THIS PROJECT. DON'T GET A FALSE ASSUMPTION THAT SINCE #
  21. # THEY USE A PUSH-OVER LICENSE, I GONNA DO THE SAME. I'M NOT. #
  22. # #
  23. # THE LICENSE CHOSEN FOR THIS PROJECT WILL PROTECT THE 4 ESSENTIAL #
  24. # FREEDOMS OF THE USER FURTHER, BY NOT ALLOWING ANY WHO TO CHANGE #
  25. # THE LICENSE AT WILL. SO NO PROPRIETARY SOFTWARE DEVELOPER COULD #
  26. # TAKE THIS CODE AND MAKE THEIR USER-SUBJUGATING SOFTWARE FROM IT. #
  27. # #
  28. #####################################################################
  29. # This file will contain elements used a lot with in the application.
  30. # They are all built upon GTK, so it's implementable without using these
  31. # but it may simplify your modification. Since the elements in will be
  32. # specific to making something like this application easier.
  33. import os
  34. import time
  35. import urllib.request
  36. import threading
  37. import json
  38. from subprocess import *
  39. from gi.repository import Gtk
  40. from gi.repository import Gio
  41. from gi.repository import Gdk
  42. from gi.repository import GLib
  43. from gi.repository import Pango
  44. from gi.repository import GdkPixbuf
  45. from PIL import Image, ImageSequence
  46. def icon( win, name, f="png", size=Gtk.IconSize.DND):
  47. # This function returns a fitting icon of the current theme,
  48. # or if not available from another theme on the system.
  49. # If a custom icon theme is set, it returns the icon from the corresponding folder.
  50. if Gtk.IconTheme.get_default().has_icon(name):
  51. return Gtk.Image.new_from_icon_name(name, size)
  52. else:
  53. # Real GTK Spinner for loading ? Why not?
  54. if name == "loading":
  55. s = Gtk.Spinner()
  56. s.set_size_request(32,32)
  57. s.start()
  58. return s
  59. else:
  60. return Gtk.HBox()
  61. def resize_gif(filename, new_file, size):
  62. # This function will resize a gif
  63. gif = Image.open(filename)
  64. layers = ImageSequence.Iterator(gif)
  65. def rs(l):
  66. for i in l:
  67. rsv = i.copy()
  68. rsv.thumbnail(size, Image.ANTIALIAS)
  69. yield rsv
  70. layers = rs(layers)
  71. # Overwrite the original gif
  72. f = next(layers)
  73. f.info = gif.info
  74. f.save(new_file, save_all=True, append_images=list(layers))
  75. def load(win, calculation_function, render_function, *args, wait=True):
  76. # This function will load widgets that take time to load.
  77. # Due to the peculiarities of the GTK main thread. I need
  78. # to separate the computation and the rendering part of
  79. # the job into two distingt functions.
  80. # One will do all the job to get the file or resolve the url
  81. # or whatever it needs to do, which does not require GTK to
  82. # be done. The second will be the GTK commands. The rendering,
  83. # done with in the GTK main thread.
  84. wbox = Gtk.HBox()
  85. widget = icon(win, "loading", "gif")
  86. wbox.pack_start(widget, True, False, False)
  87. widget.loaddo = True
  88. def resolve_widget_thread(widget, wbox, wf, rf, *args):
  89. calculations = wf(*args)
  90. def gtk_schedule(calculations, rf):
  91. # It seems to be important to edit GTK only
  92. # in the main thread. This will schedule it.
  93. new_widget = rf(calculations)
  94. widget.destroy()
  95. wbox.pack_start(new_widget, True, True, True)
  96. wbox.show_all()
  97. GLib.idle_add(gtk_schedule, calculations, rf)
  98. def load_event(w,e):
  99. if w.loaddo:
  100. load_thread = threading.Thread(target=resolve_widget_thread, args=(widget, wbox, calculation_function, render_function, *args))
  101. load_thread.setDaemon(True)
  102. load_thread.start()
  103. w.loaddo = False
  104. if wait:
  105. widget.connect("draw", load_event)
  106. else:
  107. load_event(widget, False)
  108. return wbox
  109. image_cache = "/tmp/JYTRANSACTION_GTK_image_cashe/"
  110. def image_save_name(url, image_cache=image_cache):
  111. save_as = ""
  112. good = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOOPASDFGHJKLZXCVBNM_1234567890"
  113. for i in url:
  114. if i in good:
  115. save_as = save_as + i
  116. else:
  117. save_as = save_as + "_"
  118. try:
  119. os.mkdir(image_cache)
  120. except:
  121. pass
  122. save_as = image_cache+save_as
  123. return save_as
  124. def clean_image_cache():
  125. for i in os.listdir(image_cache):
  126. os.remove(image_cache+i)
  127. def net_image_calculation( url, size, save_as=False, allow_gif=False):
  128. ret = ["file", save_as]
  129. # This is when we want to load a file
  130. if save_as == "FORCELOAD":
  131. try:
  132. open(url)
  133. save_as = url
  134. except:
  135. save_as = ""
  136. if not save_as:
  137. save_as = image_save_name(url)
  138. # This function will load the image in a separate thread.
  139. try:
  140. open(save_as) # In case it's already been saved
  141. except Exception as e:
  142. pass
  143. try:
  144. urllib.request.urlretrieve(url, save_as)
  145. except Exception as e:
  146. f = open(save_as, "w")
  147. f.close()
  148. if size:
  149. try:
  150. pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(save_as, size, size)
  151. ret = ["pixbuf", pixbuf] #Gtk.Image.new_from_pixbuf(pixbuf)
  152. except Exception as e:
  153. if "image file format" in str(e):
  154. try:
  155. PILImage = Image.open(save_as).convert("RGBA")
  156. PILImage.save(save_as+".png", "png")
  157. pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(save_as+".png", size, size)
  158. ret = ["pixbuf", pixbuf]
  159. except:
  160. ret = ["file", save_as]
  161. else:
  162. try:
  163. os.rename(save_as, save_as+".gif")
  164. resize_gif(save_as+".gif", save_as+"_2.gif", [size,size])
  165. ret = ["file", save_as+"_2.gif"] # Gtk.Image.new_from_file(save_as+"_2.gif")
  166. os.remove(save_as+"_2.gif")
  167. os.rename(save_as+".gif", save_as)
  168. except Exception as e:
  169. if allow_gif:
  170. ret = ["file", save_as] #Gtk.Image.new_from_file(save_as)
  171. else:
  172. ret = ["file", save_as] #Gtk.Image.new_from_file(save_as)
  173. return ret
  174. def net_image_render(calc):
  175. # This will make the image itself.
  176. ret = Gtk.Image()
  177. if calc[0] == "file":
  178. ret = Gtk.Image.new_from_file(calc[1])
  179. elif calc[0] == "pixbuf":
  180. ret = Gtk.Image.new_from_pixbuf(calc[1])
  181. return ret