Tried improved threading logic

This commit is contained in:
Maxim Stewart 2019-07-07 18:18:51 -05:00
parent d0d316bb87
commit 3dbba19f28
28 changed files with 58 additions and 74 deletions

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 858 B

After

Width:  |  Height:  |  Size: 858 B

View File

Before

Width:  |  Height:  |  Size: 850 B

After

Width:  |  Height:  |  Size: 850 B

View File

Before

Width:  |  Height:  |  Size: 702 B

After

Width:  |  Height:  |  Size: 702 B

View File

Before

Width:  |  Height:  |  Size: 925 B

After

Width:  |  Height:  |  Size: 925 B

View File

Before

Width:  |  Height:  |  Size: 882 B

After

Width:  |  Height:  |  Size: 882 B

View File

Before

Width:  |  Height:  |  Size: 707 B

After

Width:  |  Height:  |  Size: 707 B

View File

Before

Width:  |  Height:  |  Size: 798 B

After

Width:  |  Height:  |  Size: 798 B

View File

Before

Width:  |  Height:  |  Size: 989 B

After

Width:  |  Height:  |  Size: 989 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -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

View File

@ -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):

View File

@ -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])