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
|
@ -5,6 +5,7 @@
|
|||
|
||||
import os
|
||||
import math
|
||||
import hashlib
|
||||
import threading
|
||||
|
||||
# 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:
|
||||
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
|
||||
layer.move_to(x,y+r)
|
||||
layer.arc(x+r, y+r, r, math.pi, 3*math.pi/2)
|
||||
|
@ -196,6 +207,201 @@ def blur(surface, win, amount):
|
|||
else:
|
||||
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):
|
||||
|
||||
# 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
|
||||
# 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
|
||||
# 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.
|
||||
# the old organizer.
|
||||
|
||||
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.imageload += 1 # gives tons of errors when loading single images.
|
||||
def loadimage(layer, win ,path, x, y, width, height, fit):
|
||||
|
||||
# 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
|
||||
win.images[cell][path] = {
|
||||
"loading": True, # Whether the image is currently loading.
|
||||
"image" : None, # The image data it self.
|
||||
"hash" : "" # The hash of the image file.
|
||||
}
|
||||
|
||||
#GLib.timeout_add(1, loadimage, layer, win ,path, x, y, width, height, fit)
|
||||
t = threading.Thread(target=loadimage, args=(layer, win ,path, x, y, width, height, fit))
|
||||
# Then we want to insure to load only a given amount of images in the same
|
||||
# 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()
|
||||
|
||||
#loading it back
|
||||
else:
|
||||
if win.images[cell][path] != "LOADING-IMAGE":
|
||||
imagesurface = win.images[cell][path]
|
||||
if win.images[cell][path]["image"]:
|
||||
imagesurface = win.images[cell][path]["image"]
|
||||
|
||||
# Writting the image to the screen
|
||||
layer.set_source_surface(imagesurface, x, y)
|
||||
|
|
Loading…
Reference in a new issue