from PyQt4.QtCore import *
from PyQt4.QtGui import *
from random import shuffle
from time import clock

class TreeItem(object) :
    def __init__(self, data, parent=None) :
        self.parentItem = parent
        self.itemData = data
        self.childItems = []

    def appendChild(self, item) :
        self.childItems.append(item)

    def child(self, row) :
        return self.childItems[row]

    def childCount(self) :
        return len(self.childItems)

    def columnCount(self) :
        return len(self.itemData)

    def data(self, column) :
        try :
            return self.itemData[column]
        except IndexError :
            return None

    def parent(self) :
        return self.parentItem

    def row(self) :
        if self.parentItem :
            return self.parentItem.childItems.index(self)
        return 0
    
    
class TreeModel(QAbstractItemModel) :
    def __init__(self, ) :
        QAbstractItemModel.__init__(self)

        self.root = TreeItem(("root",))
        self.fld = TreeItem(("Items",), self.root)
        self.root.appendChild(self.fld)

        self.row_id = []
        for j in xrange(1000000) :
            self.row_id.append(j)
            self.fld.appendChild(TreeItem(("Item"+str(j+1),), self.fld))
        print "Done"
            
    def _ItemFromIndex(self, index) :
        if not index.isValid() :
            return self.root
        item = index.internalPointer()
        if item == self.fld :
            return item
        fld_index = self.createIndex(0, 0, self.fld)
        return self.index(self.row_id[index.row()], 0, fld_index).internalPointer()

    def columnCount(self, parent) :
        return 1
        
    def data(self, index, role) :
        item = self._ItemFromIndex(index)
        if role == Qt.DisplayRole :
            return item.data(0)
        
    def flags(self, flag) :
        return (Qt.ItemIsEnabled|Qt.ItemIsUserCheckable|Qt.ItemIsSelectable)
    
    def hasChildren(self, parent=QModelIndex()) :
        if parent.column() > 0 :
            return False
        item = self.root if not parent.isValid() else parent.internalPointer()
        return item.childCount() != 0
    
    def index(self, row, column, parent) :
        if not self.hasIndex(row, column, parent) :
            return QModelIndex()
        
        parent = self.root if not parent.isValid() else parent.internalPointer()
        child = parent.child(row)
        return self.createIndex(row, column, child) if child else QModelIndex()
        
    def parent(self, index):
        if not index.isValid() :
            return QModelIndex()

        child = index.internalPointer()
        parent = child.parent()

        if parent == self.root :
            return QModelIndex()

        return self.createIndex(parent.row(), 0, parent)
    
    def rowCount(self, parent=QModelIndex()) :
        if parent.column() > 0 :
            return 0
        item = self.root if not parent.isValid() else parent.internalPointer()
        return item.childCount()
    

class Tree(QTreeView) :
    def __init__(self, parent, model) :
        QTreeView.__init__(self, parent)
        self.setHeaderHidden(True)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.setUniformRowHeights(True)
        self.setModel(model)
        self.expand(self.model().index(0, 0, QModelIndex()))
               
    def Shuffle(self) :
        t1 = clock()
        shuffle(self.model().row_id)
        self.model().reset()
        self.expand(self.model().index(0, 0, QModelIndex()))
        print "Shuffle time:", clock()-t1
   
class Frame(QWidget) :
    def __init__(self, parent, model=None) :
        super(QWidget, self).__init__(parent)
        
        self.tree = Tree(self, TreeModel())
        self.shuffle_btn = QPushButton("Shuffle")
        self.shuffle_btn.clicked.connect(self.tree.Shuffle)
        
        vbox = QVBoxLayout()
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.addWidget(self.tree)
        vbox.addWidget(self.shuffle_btn)
        self.setLayout(vbox)
    
if __name__ == "__main__" :
    import sys
    a = QApplication(sys.argv)
    
    dia = Frame(None)
    dia.resize(400, 600)
    dia.show()
    a.exec_()