Python 3 ThreadPoolExecutor in Gtk 3

So I wanted to collect all the theme icons on my Linux machine so I wrote a little app. It is really fast using the ThreadPoolExecutor from futures. I’ve used this at work but never in a Gtk app. Here’s the result from the straight Python 3 code without a GUI. That’s a lot of images. I didn’t realize there are that many as Icon Browser displays nowhere near that amount.

icon collection in terminal

Here’s the code.

#!/usr/bin/python3
from concurrent.futures import ThreadPoolExecutor
import os
import time

def get_icons():
    filter = ['.png','.svg']
    base_path = '/usr/share/icons'
    for (dirpath, dirnames, filenames) in os.walk(base_path):
        if not filenames: continue # If not files found just skip path.
        # Filter by extension.
        for img in filenames:
            if os.path.splitext(img)[-1].strip().lower() not in filter: 
                continue
            img_path = os.path.join(dirpath, img)
            yield img_path

if __name__ in '__main__':
    start = time.time()
    with ThreadPoolExecutor(max_workers=25) as executor:
        task = executor.submit(get_icons)
        count=0
        for img in task.result():
            count += 1
            print(img)

    print('total items: {0} took: {1} seconds'.format(count, time.time() - start))

Of course in Gtk you have added overhead but by limiting what I return it ran reasonably well. I might add a second list box to separate each theme, size, and type to reduce the overhead even more so we only load those small lists of icons on click. For this I just want ed to see if it would work as I have only used the thread library for Gtk in the past. Here’s the result.

I filter on 16px icons only. It’s still 5k worth of images. Here’s the code.

#!/usr/bin/python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk,GObject,GdkPixbuf
from concurrent.futures import ThreadPoolExecutor
import os

def get_icons():
    filter = ['.png']#,'.svg']
    base_path = '/usr/share/icons'
    for (dirpath, dirnames, filenames) in os.walk(base_path):
        if not filenames: continue # If not files found just skip path.
        if '16x16' not in dirpath.lower():
            continue # limit amount as there are too many to load up.
        # Filter by extension.
        for img in filenames:
            if os.path.splitext(img)[-1].strip().lower() not in filter: 
                continue
            img_path = os.path.join(dirpath, img)
            yield img_path

class MyWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="PyCons")
        self.set_title("PyCons")
        self.set_default_size(800, 450)

        self.button = Gtk.Button(label="Fetch Icons")
        self.button.connect("clicked",self.fetch)

        self.status = Gtk.Label()
        self.status.set_text(' Idle')
        self.status.set_halign(Gtk.Align.START)

        self.vbox = Gtk.VBox()

        self.scroller = Gtk.ScrolledWindow()
        self.listbox = Gtk.ListBox()
        self.imgIcon = Gtk.Image()

        self.scroller.add(self.listbox)

        self.vbox.pack_start(self.button,False,False,5)
        self.vbox.pack_start(self.scroller,True,True,5)
        self.vbox.pack_end(self.status,False,False,5)

        self.add(self.vbox)

    def fetch(self,button):
        count=0
        with ThreadPoolExecutor(max_workers=25) as executor:
            task = executor.submit(get_icons)
            for img_path in task.result():
                count+=1

                pixbuf = pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(img_path,16,16) 
                icon = self.imgIcon.new_from_pixbuf(pixbuf)

                name = Gtk.Label()
                name.set_text(img_path.split('/')[-1].split('.')[0].strip())

                path = Gtk.Label()
                path.set_text(img_path)

                row = Gtk.ListBoxRow()
                hbox = Gtk.HBox()
                hbox.pack_start(icon,False,False,10)
                hbox.pack_start(name,False,True,10)
                hbox.pack_end(path,False,True,1)

                row.add(hbox)
                self.listbox.insert(row,0)
                self.listbox.show_all()
        self.status.set_text(' Fetched %s items' % count)

win = MyWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()

So there you have it. Two ways to list the theme and system icons and images on your Ubuntu/Debian based system. If you need to do something heavy in a Gtk application this might be a easy solution for you.