Приветствую.
Вот решил переписать свой виджет дерева директорий на gio и заодно добавить в него автоматическое обновление при изменении в структуре директорий. Для реализации автообновления решил заюзать gio.FileMonitor и вот тут-то у меня возникли проблемы.
Во-первых т.к. gio.FileMonitor не умеет следить за изменениями в директориях рекурсивно, его приходится вешать на каждую директорию отображаемую в дереве. Соответственно при удалении директории обработчик сигнала «changed» вызывается 2 раза, один раз от монитора удаленной директории, а второй от монитора родительской директории. Это конечно не большая проблема, но может можно как-нибудь решить?
Вторая проблема намного более серьезная. При удалении и последующем создании директории с одним и тем же именем и путем, пайтон уходит в сегфолт.
Кстати еще заметил странное поведение обработчика сигнала «test-expand-row» виджета gtk.TreeView. В документации написано, что раскрытие происходит если он возвращает True. На практике все с точностью до наоборот.
Вот код:
import glib
import gtk
import gio
class DirectoryTree(gtk.TreeView):
__gsignals__ = {
"test-expand-row": "override"
}
def __init__(self, uri, show_hidden=False):
model = gtk.TreeStore(gio.File, gio.Icon, str)
super(DirectoryTree, self).__init__(model)
self.__show_hidden = show_hidden
self.__uri = uri
self.__gemblem_noread = gio.Emblem(gio.ThemedIcon("emblem-noread"),
gio.EMBLEM_ORIGIN_LIVEMETADATA)
self.__gemblem_symlink = gio.Emblem(gio.ThemedIcon(
"emblem-symbolic-link"), gio.EMBLEM_ORIGIN_LIVEMETADATA)
column = gtk.TreeViewColumn()
renderer = gtk.CellRendererPixbuf()
column.pack_start(renderer, False)
column.set_attributes(renderer, gicon=1)
renderer = gtk.CellRendererText()
column.pack_start(renderer, True)
column.set_attributes(renderer, text=2)
self.set_headers_visible(False)
# Sort files by their names
model.set_sort_column_id(2, gtk.SORT_ASCENDING)
self.append_column(column)
# Create root item
gfile = gio.File(uri)
title = gfile.query_info("standard::display-name").get_display_name()
gicon = self.__get_emblemed_icon(gfile)
iter = model.append(None, (gfile, gicon, title))
self.__install_gfile_monitor(gfile, iter)
model.append(iter)
self.__check_for_subdirs(iter)
def __check_for_subdirs(self, iter):
model = self.get_model()
gfile = model.get_value(iter, 0)
try:
gfinfos = gfile.enumerate_children("standard::type")
for i in gfinfos:
if i.get_file_type() == gio.FILE_TYPE_DIRECTORY:
return
except gio.Error:
pass
model.remove(model.iter_children(iter))
def __install_gfile_monitor(self, gfile, iter):
monitor = gfile.monitor()
monitor.connect("changed", self.__on_gfile_changed, iter)
def __on_gfile_delete(self, parent, gfile):
model = self.get_model()
child = model.iter_children(parent)
if child is None:
return
if model.get_value(child, 0) is None:
glib.idle_add(self.__check_for_subdirs, parent)
else:
while child:
stored_gfile = model.get_value(child, 0)
if gfile.equal(stored_gfile):
model.remove(child)
child = None
else:
child = model.iter_next(child)
def __on_gfile_create(self, parent, gfile):
model = self.get_model()
try:
gfinfo = gfile.query_info("standard::type,"
"standard::display-name,standard::is-hidden")
except gio.Error, e:
return
if gfinfo.get_file_type() != gio.FILE_TYPE_DIRECTORY:
return
# Not show hidden items if we not want them
if not self.__show_hidden and gfinfo.get_is_hidden():
return
child = model.iter_children(parent)
if child is None:
model.append(parent)
elif model.get_value(child, 0) is not None:
gicon = self.__get_emblemed_icon(gfile)
title = gfinfo.get_display_name()
child = model.append(parent, (gfile, gicon, title))
self.__install_gfile_monitor(gfile, child)
model.append(child)
glib.idle_add(self.__check_for_subdirs, child)
def __on_gfile_changed(self, monitor, gfile, other_gfile, event_type, iter):
if event_type == gio.FILE_MONITOR_EVENT_CREATED:
self.__on_gfile_create(iter, gfile)
elif event_type == gio.FILE_MONITOR_EVENT_DELETED:
self.__on_gfile_delete(iter, gfile)
def __get_emblemed_icon(self, gfile):
gfinfo = gfile.query_info("standard::icon,standard::is-symlink,"
"access::can-read,access::can-write")
if not gfinfo.get_attribute_boolean("access::can-read"):
gicon = gio.EmblemedIcon(gfinfo.get_icon(), self.__gemblem_noread)
elif gfinfo.get_attribute_boolean("standard::is-symlink"):
gicon = gio.EmblemedIcon(gfinfo.get_icon(), self.__gemblem_symlink)
else:
gicon = gfinfo.get_icon()
return gicon
def __fill_model(self, iter):
model = self.get_model()
gfile = model.get_value(iter, 0)
gfinfos = gfile.enumerate_children("standard::type,standard::name,"
"standard::display-name,standard::is-hidden")
for i in gfinfos:
if i.get_file_type() != gio.FILE_TYPE_DIRECTORY:
continue
# Not show hidden items if we not want them
if not self.__show_hidden and i.get_is_hidden():
continue
child = gfile.get_child(i.get_name())
gicon = self.__get_emblemed_icon(child)
title = i.get_display_name()
child_iter = model.append(iter, (child, gicon, title))
self.__install_gfile_monitor(child, child_iter)
model.append(child_iter)
glib.idle_add(self.__check_for_subdirs, child_iter)
def do_test_expand_row(self, iter, path):
model = self.get_model()
child = model.iter_children(iter)
if model.get_value(child, 0) is None:
model.remove(child)
self.__fill_model(iter)
def main():
window = gtk.Window()
window.set_title("Directory Tree")
window.set_default_size(600, 400)
window.connect("destroy", gtk.main_quit)
sw = gtk.ScrolledWindow()
dirtree = DirectoryTree("file:///")
sw.add(dirtree)
window.add(sw)
window.show_all()
gtk.main()
if __name__ == "__main__":
main()