Image loading Mechanism Changed
Redone the loading and reloading of images. Now it's using MD5 hashes to check for difference when reloading. There is a reload function Also to colors added ability to refresh as well
This commit is contained in:
parent
f6cf881283
commit
d6c0e4d06a
1 changed files with 238 additions and 139 deletions
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import math
|
import math
|
||||||
|
import hashlib
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
# GTK module ( Graphical interface
|
# GTK module ( Graphical interface
|
||||||
|
@ -93,6 +94,16 @@ def roundrect(layer, win, x, y, width, height, r, button=False, icon=False,
|
||||||
if height < r*2:
|
if height < r*2:
|
||||||
height = r*2
|
height = r*2
|
||||||
|
|
||||||
|
# I just out of blue decided that I want to have a setting to restrict
|
||||||
|
# the amount of roundness for every roundrect. If the user want let him
|
||||||
|
# or her have ugly squares everywhere.
|
||||||
|
|
||||||
|
if "Roundness" not in win.settings:
|
||||||
|
win.settings["Roundness"] = 1.0
|
||||||
|
settings.write("Roundness", 1.0)
|
||||||
|
|
||||||
|
r = r*win.settings["Roundness"]
|
||||||
|
|
||||||
# actuall drawing
|
# actuall drawing
|
||||||
layer.move_to(x,y+r)
|
layer.move_to(x,y+r)
|
||||||
layer.arc(x+r, y+r, r, math.pi, 3*math.pi/2)
|
layer.arc(x+r, y+r, r, math.pi, 3*math.pi/2)
|
||||||
|
@ -196,6 +207,201 @@ def blur(surface, win, amount):
|
||||||
else:
|
else:
|
||||||
return surface
|
return surface
|
||||||
|
|
||||||
|
def hash_file(f):
|
||||||
|
|
||||||
|
# This function will give me MD5 hashes of various files
|
||||||
|
|
||||||
|
try:
|
||||||
|
BLOCKSIZE = 65536
|
||||||
|
hasher = hashlib.md5()
|
||||||
|
with open(f, 'rb') as afile:
|
||||||
|
buf = afile.read(BLOCKSIZE)
|
||||||
|
while len(buf) > 0:
|
||||||
|
hasher.update(buf)
|
||||||
|
buf = afile.read(BLOCKSIZE)
|
||||||
|
return str(hasher.hexdigest())
|
||||||
|
except:
|
||||||
|
return "FOLDER"
|
||||||
|
|
||||||
|
def loadimage(layer, win ,path, x, y, width, height, fit, cell=0):
|
||||||
|
|
||||||
|
# So multiple types of the image could be loaded. It could be either
|
||||||
|
# an image file. Like png or jpeg. Either a video file. Or a blend file
|
||||||
|
# each with it's own loading problems.
|
||||||
|
|
||||||
|
# While cairo can read pngs directly. It's not the best way of doing it
|
||||||
|
# since it's not capable of reading jpegs and other files. So let's do
|
||||||
|
# something about it.
|
||||||
|
|
||||||
|
foundformat = False
|
||||||
|
|
||||||
|
# IMAGEFILES
|
||||||
|
for f in fileformats.images:
|
||||||
|
if path.endswith(f):
|
||||||
|
|
||||||
|
foundformat = True
|
||||||
|
|
||||||
|
# So this parth of the code is for loading all simple images. From
|
||||||
|
# pngs Jpegs to anything else.
|
||||||
|
|
||||||
|
# We are going to use Gtk's GdkPixbuf for this. We will need to
|
||||||
|
# convert it to cairo surface later
|
||||||
|
|
||||||
|
try:
|
||||||
|
load1 = GdkPixbuf.Pixbuf.new_from_file(path)
|
||||||
|
except:
|
||||||
|
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"+win.settings["Theme"]+"/icons/image.png")
|
||||||
|
|
||||||
|
# VIDEOFILES
|
||||||
|
for f in fileformats.videos:
|
||||||
|
if path.endswith(f):
|
||||||
|
|
||||||
|
foundformat = True
|
||||||
|
|
||||||
|
# Now if it's a video. It's going to be a little harder. We will need
|
||||||
|
# to use totem. It's a video player that exists in many GNU/Linux
|
||||||
|
# systems. Unfortunatly not everybody could see video previews.
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
# This is going to require a few steps.
|
||||||
|
|
||||||
|
# 1. Making a filename for our totem to output.
|
||||||
|
part = path.replace("/", "_").replace(" ", "_")
|
||||||
|
|
||||||
|
# 2. Calling totem. And telling him that we want a thumbnail.
|
||||||
|
os.system("totem-video-thumbnailer -s "+str(width)+" "+path\
|
||||||
|
+" /tmp/vcstudio_thumbnail"+part+".png")
|
||||||
|
|
||||||
|
# 3. Loading this thumbnail.
|
||||||
|
load1 = GdkPixbuf.Pixbuf.new_from_file("/tmp/vcstudio_thumbnail"+part+".png")
|
||||||
|
|
||||||
|
# 4. Cleaning the thumbnail from the OS.
|
||||||
|
try:
|
||||||
|
os.remove("/tmp/vcstudio_thumbnail"+part+".png")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
except:
|
||||||
|
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"+win.settings["Theme"]+"/icons/video.png")
|
||||||
|
|
||||||
|
# BLEND FILES
|
||||||
|
|
||||||
|
for f in ["blend", "blend1"]:
|
||||||
|
if path.endswith(f):
|
||||||
|
|
||||||
|
foundformat = True
|
||||||
|
|
||||||
|
# Similarly to the Video. Blends files has their own thumbnailer. This
|
||||||
|
# time it's inside of the code of VCStudio. But I've copied it from
|
||||||
|
# the blender's installation folder. So you can find it there too
|
||||||
|
# it's called blender-thumbnailer.py. Which is a python script. Which
|
||||||
|
# is cool.
|
||||||
|
|
||||||
|
# Because thumbnailer is developed to work regardless of whether blender
|
||||||
|
# is installed or not. As it's reading the blend file directly. And
|
||||||
|
# not using some bpy script. We can copy that over to VCStudio and use
|
||||||
|
# it to give previews to users who do not have blender installed.
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
# This is going to require a few steps.
|
||||||
|
|
||||||
|
# 1. Making a filename for our thumbnailer to output.
|
||||||
|
part = path.replace("/", "_").replace(" ", "_")
|
||||||
|
|
||||||
|
# 2. Calling thumbnailer. And telling him that we want a thumbnail.
|
||||||
|
os.system("python3 "+os.getcwd()+"/UI/blender-thumbnailer.py "\
|
||||||
|
+path+" /tmp/vcstudio_thumbnail"+part+".png")
|
||||||
|
|
||||||
|
# 3. Loading this thumbnail.
|
||||||
|
load1 = GdkPixbuf.Pixbuf.new_from_file("/tmp/vcstudio_thumbnail"+part+".png")
|
||||||
|
|
||||||
|
# 4. Cleaning the thumbnail from the OS.
|
||||||
|
try:
|
||||||
|
os.remove("/tmp/vcstudio_thumbnail"+part+".png")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
except:
|
||||||
|
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"+win.settings["Theme"]+"/icons/blender.png")
|
||||||
|
|
||||||
|
|
||||||
|
if not foundformat:
|
||||||
|
|
||||||
|
# If you can't find any format. Just use the file icon then
|
||||||
|
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"+win.settings["Theme"]+"/icons/file.png")
|
||||||
|
|
||||||
|
# Then to convert the pixbuf to a cairo surface
|
||||||
|
Px = load1.get_width()
|
||||||
|
Py = load1.get_height()
|
||||||
|
load2 = cairo.ImageSurface(cairo.FORMAT_ARGB32, Px, Py)
|
||||||
|
imagedraw = cairo.Context(load2)
|
||||||
|
Gdk.cairo_set_source_pixbuf( imagedraw, load1, 0, 0)
|
||||||
|
imagedraw.paint()
|
||||||
|
|
||||||
|
# If I want to resize the image for an icon or something. There is
|
||||||
|
# gonna be the folowing algorythm.
|
||||||
|
|
||||||
|
if width or height:
|
||||||
|
|
||||||
|
dx = 0
|
||||||
|
dy = 0
|
||||||
|
|
||||||
|
|
||||||
|
imagesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
|
||||||
|
imagedraw = cairo.Context(imagesurface)
|
||||||
|
|
||||||
|
# Crop effect. Really hard on my brains.
|
||||||
|
|
||||||
|
if fit == 'crop':
|
||||||
|
if height > load2.get_height()\
|
||||||
|
or width > load2.get_width():
|
||||||
|
|
||||||
|
dx = (width/2) -(load2.get_width() /2)
|
||||||
|
dy = (height/2)-(load2.get_height()/2)
|
||||||
|
|
||||||
|
else:
|
||||||
|
factor = 1
|
||||||
|
if (load2.get_height()*(width/load2.get_width()))\
|
||||||
|
< height:
|
||||||
|
factor = height / load2.get_height()
|
||||||
|
else:
|
||||||
|
factor = width / load2.get_width()
|
||||||
|
#factor = 0.1
|
||||||
|
imagedraw.scale(factor, factor)
|
||||||
|
dx = (width/2)/factor -(load2.get_width() /2)
|
||||||
|
dy = (height/2)/factor -(load2.get_height()/2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
imagedraw.set_source_surface(load2, dx, dy)
|
||||||
|
imagedraw.paint()
|
||||||
|
else:
|
||||||
|
imagesurface = load2
|
||||||
|
|
||||||
|
# Saving it into the win.images
|
||||||
|
win.images[cell][path]["loading"] = False
|
||||||
|
win.images[cell][path]["image"] = imagesurface
|
||||||
|
win.images[cell][path]["hash"] = hash_file(path)
|
||||||
|
|
||||||
|
win.imageload -= 1
|
||||||
|
|
||||||
|
def reload_images(win):
|
||||||
|
|
||||||
|
# This function will force the images to refrash. For things like sync
|
||||||
|
# or simply where a file is changed. Or for any other reason. Now we
|
||||||
|
# probably don't need to do this on every image. So instead we are going
|
||||||
|
# to compare the hashes of the current images to the ones of the files
|
||||||
|
# and if they changed. We are going to update those.
|
||||||
|
|
||||||
|
for cell in list(win.images.keys()):
|
||||||
|
for image in list(win.images[cell].keys()):
|
||||||
|
if hash_file(image) != win.images[cell][image]["hash"]:
|
||||||
|
win.images[cell][image]["loading"] = True
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
def image(layer, win ,path, x, y, width=0, height=0, fit="crop", cell=0):
|
def image(layer, win ,path, x, y, width=0, height=0, fit="crop", cell=0):
|
||||||
|
|
||||||
# This module will handle drawing images to the layers. It's not that hard
|
# This module will handle drawing images to the layers. It's not that hard
|
||||||
|
@ -219,153 +425,46 @@ def image(layer, win ,path, x, y, width=0, height=0, fit="crop", cell=0):
|
||||||
|
|
||||||
# If you ran this software you probably noticed that images are loading
|
# If you ran this software you probably noticed that images are loading
|
||||||
# dynamically. I did it using this following crazy algorythm borowed from
|
# dynamically. I did it using this following crazy algorythm borowed from
|
||||||
# the old organizer. Basically for each image I see it's any image is loading
|
# the old organizer.
|
||||||
# at the moment. If yes, wait. If not loading. Meaning it can take the turn
|
|
||||||
# I create a thread using GLib. Because I'm lazy. Which is loading the image.
|
|
||||||
# Basically the UI keeps working before all images are loaded.
|
|
||||||
|
|
||||||
if path not in win.images[cell] or win.images[cell][path] == "LOADING-IMAGE":
|
# Here I want to load images. But I also want to give myself an ability to
|
||||||
|
# load refrash images. Without refrashing all the images. For this I'm going
|
||||||
|
# to use hashes. Specificly MD5 but it doesn't matter really. The idea is
|
||||||
|
# that on a special redraw call. We are going to check hashes storred with
|
||||||
|
# those from the file. And only if a file really changed. Then update the image.
|
||||||
|
|
||||||
|
if path not in win.images[cell] or win.images[cell][path]["loading"]:
|
||||||
|
|
||||||
win.images[cell][path] = "LOADING-IMAGE"
|
# If this is the first time this image is draw we want to create it a data
|
||||||
|
# structure. So we could load the image.
|
||||||
|
|
||||||
if win.imageload < 1: # This needs to be fixed. Because multithreading
|
win.images[cell][path] = {
|
||||||
win.imageload += 1 # gives tons of errors when loading single images.
|
"loading": True, # Whether the image is currently loading.
|
||||||
def loadimage(layer, win ,path, x, y, width, height, fit):
|
"image" : None, # The image data it self.
|
||||||
|
"hash" : "" # The hash of the image file.
|
||||||
# Loading the image into the cairo.
|
}
|
||||||
try:
|
|
||||||
# It could be not PNG
|
|
||||||
try:
|
|
||||||
loadimage = cairo.ImageSurface.create_from_png(path)
|
|
||||||
except:
|
|
||||||
# If it's not png it's gonna take few steps
|
|
||||||
|
|
||||||
# Let's first of all see if it's a blend file.
|
|
||||||
|
|
||||||
part = path.replace("/", "_").replace(" ", "_")
|
|
||||||
|
|
||||||
if path.endswith(".blend") or path.endswith(".blend1"):
|
|
||||||
|
|
||||||
IF = os.system("python3 "+os.getcwd()+"/UI/blender-thumbnailer.py "\
|
|
||||||
+path+" /tmp/vcstudio_blender_thumbnail"+part+".png")
|
|
||||||
if not IF:
|
|
||||||
load1 = GdkPixbuf.Pixbuf.new_from_file("/tmp/vcstudio_blender_thumbnail"+part+".png")
|
|
||||||
else:
|
|
||||||
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"\
|
|
||||||
+win.settings["Theme"]+"/icons/blender.png")
|
|
||||||
|
|
||||||
os.remove("/tmp/vcstudio_blender_thumbnail"+part+".png")
|
|
||||||
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
# if video
|
|
||||||
video = False
|
|
||||||
for f in fileformats.videos:
|
|
||||||
if path.endswith(f):
|
|
||||||
video = True
|
|
||||||
|
|
||||||
if video:
|
|
||||||
IF = os.system("totem-video-thumbnailer -s "+str(width)+" "+path\
|
|
||||||
+" /tmp/vcstudio_video_thumbnail"+part+".png")
|
|
||||||
if not IF:
|
|
||||||
load1 = GdkPixbuf.Pixbuf.new_from_file("/tmp/vcstudio_video_thumbnail"+part+".png")
|
|
||||||
else:
|
|
||||||
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"\
|
|
||||||
+win.settings["Theme"]+"/icons/video.png")
|
|
||||||
os.remove("/tmp/vcstudio_video_thumbnail"+part+".png")
|
|
||||||
else:
|
|
||||||
# if image
|
|
||||||
image = False
|
|
||||||
for f in fileformats.images:
|
|
||||||
if path.endswith(f):
|
|
||||||
image = True
|
|
||||||
|
|
||||||
|
|
||||||
if image:
|
|
||||||
try:
|
|
||||||
load1 = GdkPixbuf.Pixbuf.new_from_file(path)
|
|
||||||
except:
|
|
||||||
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"\
|
|
||||||
+win.settings["Theme"]+"/icons/image.png")
|
|
||||||
|
|
||||||
else:
|
|
||||||
|
|
||||||
if path.endswith(".progress"):
|
|
||||||
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"\
|
|
||||||
+win.settings["Theme"]+"/icons/checklist.png")
|
|
||||||
else:
|
|
||||||
load1 = GdkPixbuf.Pixbuf.new_from_file("settings/themes/"\
|
|
||||||
+win.settings["Theme"]+"/icons/file.png")
|
|
||||||
|
|
||||||
# First we need to make a pixbuf.
|
|
||||||
|
|
||||||
Px = load1.get_width()
|
|
||||||
Py = load1.get_height()
|
|
||||||
|
|
||||||
# Then to convert the pixbuf to a cairo surface
|
|
||||||
loadimage = cairo.ImageSurface(cairo.FORMAT_ARGB32, Px, Py)
|
|
||||||
imagedraw = cairo.Context(loadimage)
|
|
||||||
Gdk.cairo_set_source_pixbuf( imagedraw, load1, 0, 0)
|
|
||||||
imagedraw.paint()
|
|
||||||
|
|
||||||
# If I want to resize the image for an icon or something. There is
|
|
||||||
# gonna be the folowing algorythm.
|
|
||||||
|
|
||||||
if width or height:
|
|
||||||
|
|
||||||
dx = 0
|
|
||||||
dy = 0
|
|
||||||
|
|
||||||
|
|
||||||
imagesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
|
|
||||||
imagedraw = cairo.Context(imagesurface)
|
|
||||||
|
|
||||||
# Crop effect. Really hard on my brains.
|
|
||||||
|
|
||||||
if fit == 'crop':
|
|
||||||
if height > loadimage.get_height()\
|
|
||||||
or width > loadimage.get_width():
|
|
||||||
|
|
||||||
dx = (width/2) -(loadimage.get_width() /2)
|
|
||||||
dy = (height/2)-(loadimage.get_height()/2)
|
|
||||||
|
|
||||||
else:
|
|
||||||
factor = 1
|
|
||||||
if (loadimage.get_height()*(width/loadimage.get_width()))\
|
|
||||||
< height:
|
|
||||||
factor = height / loadimage.get_height()
|
|
||||||
else:
|
|
||||||
factor = width / loadimage.get_width()
|
|
||||||
#factor = 0.1
|
|
||||||
imagedraw.scale(factor, factor)
|
|
||||||
dx = (width/2)/factor -(loadimage.get_width() /2)
|
|
||||||
dy = (height/2)/factor -(loadimage.get_height()/2)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
imagedraw.set_source_surface(loadimage, dx, dy)
|
|
||||||
imagedraw.paint()
|
|
||||||
|
|
||||||
|
|
||||||
else:
|
|
||||||
imagesurface = loadimage
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
imagesurface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 10, 10)
|
|
||||||
|
|
||||||
# Saving it into the win.images
|
|
||||||
win.images[cell][path] = imagesurface
|
|
||||||
win.imageload -= 1
|
|
||||||
|
|
||||||
#GLib.timeout_add(1, loadimage, layer, win ,path, x, y, width, height, fit)
|
# Then we want to insure to load only a given amount of images in the same
|
||||||
t = threading.Thread(target=loadimage, args=(layer, win ,path, x, y, width, height, fit))
|
# time. For this we are going to use the next variable.
|
||||||
|
|
||||||
|
MAXLOAD = 10
|
||||||
|
|
||||||
|
if win.imageload < MAXLOAD:
|
||||||
|
win.imageload += 1
|
||||||
|
|
||||||
|
|
||||||
|
# Now we want to load the image. But we are going to do that in a separate thread
|
||||||
|
# not to freeze the UI while the image is loading.
|
||||||
|
|
||||||
|
# See: loadimage() function for the continuation of this.
|
||||||
|
|
||||||
|
t = threading.Thread(target=loadimage, args=(layer, win ,path, x, y, width, height, fit, cell))
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
#loading it back
|
#loading it back
|
||||||
else:
|
else:
|
||||||
if win.images[cell][path] != "LOADING-IMAGE":
|
if win.images[cell][path]["image"]:
|
||||||
imagesurface = win.images[cell][path]
|
imagesurface = win.images[cell][path]["image"]
|
||||||
|
|
||||||
# Writting the image to the screen
|
# Writting the image to the screen
|
||||||
layer.set_source_surface(imagesurface, x, y)
|
layer.set_source_surface(imagesurface, x, y)
|
||||||
|
|
Loading…
Reference in a new issue