secret
Last active

Gtk Tree multithreading example in Python

  • Download Gist
gtk-tree-threading-example.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
#!/usr/bin/python
import os
import threading
import time
from itertools import cycle
 
from gi.repository import GObject, Gtk
GObject.threads_init() # all Gtk is in the main thread;
# only GObject.idle_add() is in the background thread
 
 
HEARTBEAT = 20 # Hz
CHUNKSIZE = 100 # how many items to process in a single idle_add() callback
 
 
def chunks(seq, chunksize):
"""Yield N items at a time from seq."""
for i in xrange(0, len(seq), chunksize):
yield seq[i:i + chunksize]
 
 
class TreeStore(Gtk.TreeStore):
__gtype_name__ = 'TreeStore'
 
def __init__(self, topdir, done_callback=None):
Gtk.TreeStore.__init__(self, str) # super() doesn't work here
 
self.path2treeiter = {topdir: None} # path -> treeiter
self.topdir = topdir
self.done_callback = done_callback
self._cv = threading.Condition()
 
t = threading.Thread(target=self._build_tree)
t.daemon = True
t.start() # start background thread
 
def _build_tree(self, _sentinel=object()):
# executed in a background thread
cv = self._cv
p = self.path2treeiter
for dirpath, dirs, files in os.walk(self.topdir):
# wait until dirpath is appended to the tree
cv.acquire()
while p.get(dirpath, _sentinel) is _sentinel:
cv.wait()
parent = p[dirpath]
cv.release()
 
# populate tree store
dirs[:] = sorted(d for d in dirs
if d[0] != '.') # skip hidden dirs
for chunk in chunks(dirs, CHUNKSIZE):
GObject.idle_add(self._appenddir, chunk, parent, dirpath)
 
for chunk in chunks(sorted(files), CHUNKSIZE):
GObject.idle_add(self._appendfile, chunk, parent)
GObject.idle_add(self.done_callback)
 
def _appenddir(self, chunk, parent, dirpath):
# executed in the main thread
self._cv.acquire()
p = self.path2treeiter
for d in chunk:
p[os.path.join(dirpath, d)] = self.append(parent, [d])
self._cv.notify()
self._cv.release()
 
def _appendfile(self, chunk, parent):
# executed in the main thread
for f in chunk:
self.append(parent, [f])
 
 
class Window(Gtk.Window):
__gtype_name__ = 'Window'
 
def __init__(self, topdir):
super(Window, self).__init__(type=Gtk.WindowType.TOPLEVEL)
self.__start_time = time.time()
self.__title = 'GTK Tree MultiThreading Demo'
self.set_title(self.__title)
self.set_default_size(640, 480)
 
# create tree
tree_store = TreeStore(topdir, self._on_tree_completed)
tree_view = Gtk.TreeView()
tree_view.set_model(tree_store)
 
cell = Gtk.CellRendererText()
tree_view.append_column(Gtk.TreeViewColumn(topdir, cell, text=0))
scrolled_window = Gtk.ScrolledWindow()
scrolled_window.add(tree_view)
self.add(scrolled_window)
 
# update title to show that we are alive
self._update_id = GObject.timeout_add(int(1e3 / HEARTBEAT),
self._update_title)
 
def _on_tree_completed(self):
if self._update_id is None:
return
# stop updates
GObject.source_remove(self._update_id)
self._update_id = None
self.set_title('%s %s %.1f' % (self.__title, ' (done)',
time.time() - self.__start_time))
 
def _update_title(self, _suff=cycle('/|\-')):
self.set_title('%s %s %.1f' % (self.__title, next(_suff),
time.time() - self.__start_time))
return True # continue updates
 
 
win = Window(topdir=os.path.expanduser('~'))
win.connect('delete-event', Gtk.main_quit)
win.show_all()
Gtk.main()

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.