Tried improved threading logic
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 858 B After Width: | Height: | Size: 858 B |
Before Width: | Height: | Size: 850 B After Width: | Height: | Size: 850 B |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 702 B |
Before Width: | Height: | Size: 925 B After Width: | Height: | Size: 925 B |
Before Width: | Height: | Size: 882 B After Width: | Height: | Size: 882 B |
Before Width: | Height: | Size: 707 B After Width: | Height: | Size: 707 B |
Before Width: | Height: | Size: 798 B After Width: | Height: | Size: 798 B |
Before Width: | Height: | Size: 989 B After Width: | Height: | Size: 989 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@ -31,7 +31,7 @@ class Settings:
|
|||||||
self.usrHome = os.path.expanduser('~')
|
self.usrHome = os.path.expanduser('~')
|
||||||
self.desktopPath = self.usrHome + "/Desktop"
|
self.desktopPath = self.usrHome + "/Desktop"
|
||||||
self.iconContainerWxH = [128, 128]
|
self.iconContainerWxH = [128, 128]
|
||||||
self.systemIconImageWxH = [72, 72]
|
self.systemIconImageWxH = [56, 56]
|
||||||
self.viIconWxH = [256, 128]
|
self.viIconWxH = [256, 128]
|
||||||
|
|
||||||
self.DEFAULTCOLOR = gdk.RGBA(0.0, 0.0, 0.0, 0.0) # ~#00000000
|
self.DEFAULTCOLOR = gdk.RGBA(0.0, 0.0, 0.0, 0.0) # ~#00000000
|
@ -21,8 +21,10 @@ from utils.FileHandler import FileHandler
|
|||||||
def threaded(fn):
|
def threaded(fn):
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
|
threading.Thread(target=fn, args=args, kwargs=kwargs).start()
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
class Grid:
|
class Grid:
|
||||||
def __init__(self, grid, settings):
|
def __init__(self, grid, settings):
|
||||||
self.grid = grid
|
self.grid = grid
|
||||||
@ -31,13 +33,12 @@ class Grid:
|
|||||||
|
|
||||||
self.store = gtk.ListStore(GdkPixbuf.Pixbuf, str)
|
self.store = gtk.ListStore(GdkPixbuf.Pixbuf, str)
|
||||||
self.usrHome = settings.returnUserHome()
|
self.usrHome = settings.returnUserHome()
|
||||||
|
self.hideHiddenFiles = settings.isHideHiddenFiles()
|
||||||
self.builder = settings.returnBuilder()
|
self.builder = settings.returnBuilder()
|
||||||
self.ColumnSize = settings.returnColumnSize()
|
self.ColumnSize = settings.returnColumnSize()
|
||||||
self.vidsFilter = settings.returnVidsFilter()
|
self.vidsFilter = settings.returnVidsFilter()
|
||||||
self.imagesFilter = settings.returnImagesFilter()
|
self.imagesFilter = settings.returnImagesFilter()
|
||||||
self.iconFactory = Icon(settings)
|
self.iconFactory = Icon(settings)
|
||||||
self.gtkLock = False # Thread checks for gtkLock
|
|
||||||
self.threadLock = False # Gtk checks for thread lock
|
|
||||||
self.helperThread = None # Helper thread object
|
self.helperThread = None # Helper thread object
|
||||||
self.toWorkPool = [] # Thread fills pool and gtk empties it
|
self.toWorkPool = [] # Thread fills pool and gtk empties it
|
||||||
self.selectedFiles = []
|
self.selectedFiles = []
|
||||||
@ -51,8 +52,6 @@ class Grid:
|
|||||||
|
|
||||||
|
|
||||||
def setNewDirectory(self, path):
|
def setNewDirectory(self, path):
|
||||||
self.store.clear()
|
|
||||||
|
|
||||||
self.currentPath = path
|
self.currentPath = path
|
||||||
dirPaths = ['.', '..']
|
dirPaths = ['.', '..']
|
||||||
vids = []
|
vids = []
|
||||||
@ -62,15 +61,18 @@ class Grid:
|
|||||||
|
|
||||||
for f in listdir(path):
|
for f in listdir(path):
|
||||||
file = join(path, f)
|
file = join(path, f)
|
||||||
if self.settings.isHideHiddenFiles():
|
if self.hideHiddenFiles:
|
||||||
if f.startswith('.'):
|
if f.startswith('.'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if isfile(file):
|
if isfile(file):
|
||||||
if file.lower().endswith(self.vidsFilter):
|
lowerName = file.lower()
|
||||||
|
|
||||||
|
if lowerName.endswith(self.vidsFilter):
|
||||||
vids.append(f)
|
vids.append(f)
|
||||||
elif file.lower().endswith(self.imagesFilter):
|
elif lowerName.endswith(self.imagesFilter):
|
||||||
images.append(f)
|
images.append(f)
|
||||||
elif file.lower().endswith((".desktop",)):
|
elif lowerName.endswith((".desktop",)):
|
||||||
desktop.append(f)
|
desktop.append(f)
|
||||||
else:
|
else:
|
||||||
files.append(f)
|
files.append(f)
|
||||||
@ -82,53 +84,38 @@ class Grid:
|
|||||||
images.sort()
|
images.sort()
|
||||||
desktop.sort()
|
desktop.sort()
|
||||||
files.sort()
|
files.sort()
|
||||||
files = dirPaths + vids + images + desktop + files
|
|
||||||
|
|
||||||
if self.helperThread:
|
files = dirPaths + vids + images + desktop + files
|
||||||
self.helperThread.terminate()
|
self.store.clear()
|
||||||
self.helperThread = None
|
|
||||||
|
|
||||||
# Run helper thread...
|
# Run helper thread...
|
||||||
self.threadLock = True
|
self.helperThread = threading.Thread(target=self.generateGridIcons, args=(path, files))
|
||||||
self.helperThread = threading.Thread(target=self.generateGridIcon, args=(path, files)).start()
|
self.helperThread.daemon = True # Set this thread as a Daemon Thread
|
||||||
glib.idle_add(self.addToGrid, (file,)) # NOTE: This must stay in the main thread b/c
|
self.helperThread.start()
|
||||||
# gtk isn't thread safe/aware So, we
|
# self.generateGridIcons(path, files)
|
||||||
# make a sad lil thread hot potato 'game'
|
glib.idle_add(self.addToGrid, (files,)) # NOTE: This must stay in the main thread b/c
|
||||||
# out of this process.
|
# gtk isn't thread safe/aware.
|
||||||
|
|
||||||
|
|
||||||
# @threaded
|
# @threaded
|
||||||
def generateGridIcon(self, dirPath, files):
|
def generateGridIcons(self, dirPath, files):
|
||||||
# NOTE: We'll be passing pixbuf after retreval to keep Icon.py file more
|
|
||||||
# universaly usable. We can just remove get_pixbuf to get a gtk.Image type
|
|
||||||
for file in files:
|
for file in files:
|
||||||
image = self.iconFactory.createIcon(dirPath, file)
|
image = self.iconFactory.createIcon(dirPath, file)
|
||||||
self.toWorkPool.append([image.get_pixbuf(), file])
|
self.toWorkPool.append([image, file])
|
||||||
self.threadLock = False
|
|
||||||
self.gtkLock = True
|
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: If nothing else is updating, this function gets called immediatly when return is True.
|
||||||
|
# Returning False ends checks and "continues normal flow"
|
||||||
def addToGrid(self, args):
|
def addToGrid(self, args):
|
||||||
# NOTE: Returning true tells gtk to check again in the future when idle.
|
|
||||||
# False ends checks and "continues normal flow"
|
|
||||||
files = args[0]
|
files = args[0]
|
||||||
|
|
||||||
if len(self.toWorkPool) > 0:
|
if len(self.toWorkPool) > 0:
|
||||||
for dataSet in self.toWorkPool:
|
for dataSet in self.toWorkPool:
|
||||||
self.store.append(dataSet)
|
self.store.append([dataSet[0].get_pixbuf(), dataSet[1]])
|
||||||
|
|
||||||
if len(self.store) == len(files): # Confirm processed all files and cleanup
|
|
||||||
self.gtkLock = False
|
|
||||||
self.threadLock = False
|
|
||||||
self.toWorkPool.clear()
|
self.toWorkPool.clear()
|
||||||
|
if len(self.store) == len(files): # Processed all files
|
||||||
return False
|
return False
|
||||||
# Check again when idle; If nothing else is updating, this function
|
else: # Check again when idle
|
||||||
# gets called immediatly. So, we play hot potato by setting lock to Thread
|
|
||||||
else:
|
|
||||||
self.toWorkPool.clear()
|
|
||||||
self.gtkLock = False
|
|
||||||
self.threadLock = True
|
|
||||||
time.sleep(.005) # Fixes refresh and up icon not being added.
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def iconDblLeftClick(self, widget, item):
|
def iconDblLeftClick(self, widget, item):
|
@ -5,7 +5,6 @@ gi.require_version('Gdk', '3.0')
|
|||||||
|
|
||||||
from gi.repository import Gtk as gtk
|
from gi.repository import Gtk as gtk
|
||||||
from gi.repository import Gio as gio
|
from gi.repository import Gio as gio
|
||||||
from gi.repository import GdkPixbuf
|
|
||||||
from xdg.DesktopEntry import DesktopEntry
|
from xdg.DesktopEntry import DesktopEntry
|
||||||
|
|
||||||
# Python Imports
|
# Python Imports
|
||||||
@ -50,10 +49,10 @@ class Icon:
|
|||||||
if isfile(hashImgPth) == False:
|
if isfile(hashImgPth) == False:
|
||||||
self.generateVideoThumbnail(fullPath, hashImgPth)
|
self.generateVideoThumbnail(fullPath, hashImgPth)
|
||||||
|
|
||||||
thumbnl = self.createIconImageBuffer(hashImgPth, self.viIconWH)
|
thumbnl = self.createScaledImage(hashImgPth, self.viIconWH)
|
||||||
# Image Icon
|
# Image Icon
|
||||||
elif file.lower().endswith(self.imagesList):
|
elif file.lower().endswith(self.imagesList):
|
||||||
thumbnl = self.createIconImageBuffer(fullPath, self.viIconWH)
|
thumbnl = self.createScaledImage(fullPath, self.viIconWH)
|
||||||
# .desktop file parsing
|
# .desktop file parsing
|
||||||
elif fullPath.lower().endswith( ('.desktop',) ):
|
elif fullPath.lower().endswith( ('.desktop',) ):
|
||||||
thumbnl = self.parseDesktopFiles(fullPath)
|
thumbnl = self.parseDesktopFiles(fullPath)
|
||||||
@ -91,7 +90,7 @@ class Icon:
|
|||||||
hashImgPth = steamIconsDir + fileHash + ".jpg"
|
hashImgPth = steamIconsDir + fileHash + ".jpg"
|
||||||
if isfile(hashImgPth) == True:
|
if isfile(hashImgPth) == True:
|
||||||
# Use video sizes since headers are bigger
|
# Use video sizes since headers are bigger
|
||||||
return self.createIconImageBuffer(hashImgPth, self.viIconWH)
|
return self.createScaledImage(hashImgPth, self.viIconWH)
|
||||||
|
|
||||||
execStr = xdgObj.getExec()
|
execStr = xdgObj.getExec()
|
||||||
parts = execStr.split("steam://rungameid/")
|
parts = execStr.split("steam://rungameid/")
|
||||||
@ -110,9 +109,9 @@ class Icon:
|
|||||||
proc.wait()
|
proc.wait()
|
||||||
|
|
||||||
# Use video sizes since headers are bigger
|
# Use video sizes since headers are bigger
|
||||||
return self.createIconImageBuffer(hashImgPth, self.viIconWH)
|
return self.createScaledImage(hashImgPth, self.viIconWH)
|
||||||
elif os.path.exists(icon):
|
elif os.path.exists(icon):
|
||||||
return self.createIconImageBuffer(icon, self.systemIconImageWH)
|
return self.createScaledImage(icon, self.systemIconImageWH)
|
||||||
else:
|
else:
|
||||||
for (dirpath, dirnames, filenames) in os.walk(iconsDirs):
|
for (dirpath, dirnames, filenames) in os.walk(iconsDirs):
|
||||||
for file in filenames:
|
for file in filenames:
|
||||||
@ -121,7 +120,7 @@ class Icon:
|
|||||||
altIconPath = dirpath + "/" + file
|
altIconPath = dirpath + "/" + file
|
||||||
break
|
break
|
||||||
|
|
||||||
return self.createIconImageBuffer(altIconPath, self.systemIconImageWH)
|
return self.createScaledImage(altIconPath, self.systemIconImageWH)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
return None
|
return None
|
||||||
@ -129,17 +128,15 @@ class Icon:
|
|||||||
|
|
||||||
def getSystemThumbnail(self, filename, size):
|
def getSystemThumbnail(self, filename, size):
|
||||||
try:
|
try:
|
||||||
iconPath = None
|
|
||||||
if os.path.exists(filename):
|
if os.path.exists(filename):
|
||||||
file = gio.File.new_for_path(filename)
|
gioFile = gio.File.new_for_path(filename)
|
||||||
info = file.query_info('standard::icon' , 0 , gio.Cancellable())
|
info = gioFile.query_info('standard::icon' , 0 , gio.Cancellable())
|
||||||
icon = info.get_icon().get_names()[0]
|
icon = info.get_icon().get_names()[0]
|
||||||
iconTheme = gtk.IconTheme.get_default()
|
iconTheme = gtk.IconTheme.get_default()
|
||||||
iconFile = iconTheme.lookup_icon(icon , size , 0)
|
iconData = iconTheme.lookup_icon(icon , size , 0)
|
||||||
|
if iconData:
|
||||||
if iconFile != None:
|
iconPath = iconData.get_filename()
|
||||||
iconPath = iconFile.get_filename()
|
return gtk.Image.new_from_file(iconPath) # This seems to cause a lot of core dump issues...
|
||||||
return self.createIconImageBuffer(iconPath, self.systemIconImageWH)
|
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
@ -149,15 +146,15 @@ class Icon:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def createIconImageBuffer(self, path, wxh):
|
def createScaledImage(self, path, wxh):
|
||||||
try:
|
try:
|
||||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(path, wxh[0], wxh[1], False)
|
pixbuf = gtk.Image.new_from_file(path).get_pixbuf()
|
||||||
|
scaledPixBuf = pixbuf.scale_simple(wxh[0], wxh[1], 2) # 2 = BILINEAR and is best by default
|
||||||
|
return gtk.Image.new_from_pixbuf(scaledPixBuf)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return gtk.Image.new_from_pixbuf(pixbuf)
|
|
||||||
|
|
||||||
|
|
||||||
def generateVideoThumbnail(self, fullPath, hashImgPth):
|
def generateVideoThumbnail(self, fullPath, hashImgPth):
|
||||||
try:
|
try:
|
||||||
proc = subprocess.Popen([self.thubnailGen, "-t", "65%", "-s", "300", "-c", "jpg", "-i", fullPath, "-o", hashImgPth])
|
proc = subprocess.Popen([self.thubnailGen, "-t", "65%", "-s", "300", "-c", "jpg", "-i", fullPath, "-o", hashImgPth])
|