#!/usr/bin/env python title = "File Tree (GenericTreeModel)" description = """ This is a file list demo which makes use of the GenericTreeModel python implementation of the Gtk.TreeModel interface. This demo shows what methods need to be overridden to provide a valid TreeModel to a TreeView. """ import os import stat import time from collections import OrderedDict import pygtkcompat pygtkcompat.enable_gtk('3.0') import gtk folderxpm = [ "17 16 7 1", " c #000000", ". c #808000", "X c yellow", "o c #808080", "O c #c0c0c0", "+ c white", "@ c None", "@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@", "@@+XXXX.@@@@@@@@@", "@+OOOOOO.@@@@@@@@", "@+OXOXOXOXOXOXO. ", "@+XOXOXOXOXOXOX. ", "@+OXOXOXOXOXOXO. ", "@+XOXOXOXOXOXOX. ", "@+OXOXOXOXOXOXO. ", "@+XOXOXOXOXOXOX. ", "@+OXOXOXOXOXOXO. ", "@+XOXOXOXOXOXOX. ", "@+OOOOOOOOOOOOO. ", "@ ", "@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@" ] folderpb = gtk.gdk.pixbuf_new_from_xpm_data(folderxpm) filexpm = [ "12 12 3 1", " c #000000", ". c #ffff04", "X c #b2c0dc", "X XXX", "X ...... XXX", "X ...... X", "X . ... X", "X ........ X", "X . .... X", "X ........ X", "X . .. X", "X ........ X", "X . .. X", "X ........ X", "X X" ] filepb = gtk.gdk.pixbuf_new_from_xpm_data(filexpm) class FileTreeModel(gtk.GenericTreeModel): __gtype_name__ = 'DemoFileTreeModel' column_types = (gtk.gdk.Pixbuf, str, int, str, str) column_names = ['Name', 'Size', 'Mode', 'Last Changed'] def __init__(self, dname=None): gtk.GenericTreeModel.__init__(self) if not dname: self.dirname = os.path.expanduser('~') else: self.dirname = os.path.abspath(dname) self.files = self.build_file_dict(self.dirname) return def build_file_dict(self, dirname): """ :Returns: A dictionary containing the files in the given dirname keyed by filename. If the child filename is a sub-directory, the dict value is a dict. Otherwise it will be None. """ d = OrderedDict() for fname in os.listdir(dirname): try: filestat = os.stat(os.path.join(dirname, fname)) except OSError: d[fname] = None else: d[fname] = OrderedDict() if stat.S_ISDIR(filestat.st_mode) else None return d def get_node_from_treepath(self, path): """ :Returns: The node stored at the given tree path in local storage. """ # TreePaths are a series of integer indices so just iterate through them # and index values by each integer since we are using an OrderedDict if path is None: path = [] node = self.files for index in path: node = list(node.values())[index] return node def get_node_from_filepath(self, filepath): """ :Returns: The node stored at the given file path in local storage. """ if not filepath: return self.files node = self.files for key in filepath.split(os.path.sep): node = node[key] return node def get_column_names(self): return self.column_names[:] # # GenericTreeModel Implementation # def on_get_flags(self): return 0 def on_get_n_columns(self): return len(self.column_types) def on_get_column_type(self, n): return self.column_types[n] def on_get_path(self, relpath): path = [] node = self.files for key in relpath.split(os.path.sep): path.append(list(node.keys()).index(key)) node = node[key] return path def on_get_value(self, relpath, column): fname = os.path.join(self.dirname, relpath) try: filestat = os.stat(fname) except OSError: return None mode = filestat.st_mode if column == 0: if stat.S_ISDIR(mode): return folderpb else: return filepb elif column == 1: return os.path.basename(relpath) elif column == 2: return filestat.st_size elif column == 3: return oct(stat.S_IMODE(mode)) return time.ctime(filestat.st_mtime) def on_get_iter(self, path): filepath = '' value = self.files for index in path: filepath = os.path.join(filepath, list(value.keys())[index]) value = list(value.values())[index] return filepath def on_iter_next(self, filepath): parent_path, child_path = os.path.split(filepath) parent = self.get_node_from_filepath(parent_path) # Index of filepath within its parents child list sibling_names = list(parent.keys()) index = sibling_names.index(child_path) try: return os.path.join(parent_path, sibling_names[index + 1]) except IndexError: return None def on_iter_children(self, filepath): if filepath: children = list(self.get_node_from_filepath(filepath).keys()) if children: return os.path.join(filepath, children[0]) elif self.files: return list(self.files.keys())[0] return None def on_iter_has_child(self, filepath): return bool(self.get_node_from_filepath(filepath)) def on_iter_n_children(self, filepath): return len(self.get_node_from_filepath(filepath)) def on_iter_nth_child(self, filepath, n): try: child = list(self.get_node_from_filepath(filepath).keys())[n] if filepath: return os.path.join(filepath, child) else: return child except IndexError: return None def on_iter_parent(self, filepath): return os.path.dirname(filepath) def on_ref_node(self, filepath): value = self.get_node_from_filepath(filepath) if value is not None: value.update(self.build_file_dict(os.path.join(self.dirname, filepath))) def on_unref_node(self, filepath): pass class GenericTreeModelExample: def delete_event(self, widget, event, data=None): gtk.main_quit() return False def __init__(self): # Create a new window self.window = gtk.Window(type=gtk.WINDOW_TOPLEVEL) self.window.set_size_request(300, 200) self.window.connect("delete_event", self.delete_event) self.listmodel = FileTreeModel() # create the TreeView self.treeview = gtk.TreeView() # create the TreeViewColumns to display the data column_names = self.listmodel.get_column_names() self.tvcolumn = [None] * len(column_names) cellpb = gtk.CellRendererPixbuf() self.tvcolumn[0] = gtk.TreeViewColumn(column_names[0], cellpb, pixbuf=0) cell = gtk.CellRendererText() self.tvcolumn[0].pack_start(cell, False) self.tvcolumn[0].add_attribute(cell, 'text', 1) self.treeview.append_column(self.tvcolumn[0]) for n in range(1, len(column_names)): cell = gtk.CellRendererText() if n == 1: cell.set_property('xalign', 1.0) self.tvcolumn[n] = gtk.TreeViewColumn(column_names[n], cell, text=n + 1) self.treeview.append_column(self.tvcolumn[n]) self.scrolledwindow = gtk.ScrolledWindow() self.scrolledwindow.add(self.treeview) self.window.add(self.scrolledwindow) self.treeview.set_model(self.listmodel) self.window.set_title(self.listmodel.dirname) self.window.show_all() def main(demoapp=None): demo = GenericTreeModelExample() demo gtk.main() if __name__ == "__main__": main()