+already_in_nodes = []
+
+node_status = { "in": "Already Selected",
+ "out": "Not Selected",
+ "add": "To be Added",
+ "remove": "To be Removed"}
+
+class NodeView(QTreeView):
+ def __init__(self, parent):
+ QTreeView.__init__(self, parent)
+
+ self.setAnimated(True)
+ self.setItemsExpandable(True)
+ self.setRootIsDecorated(True)
+ self.setAlternatingRowColors(True)
+# self.setSelectionMode(self.MultiSelection)
+ self.setAttribute(Qt.WA_MacShowFocusRect, 0)
+ self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
+ self.setToolTip("Double click on a row to change its status")
+
+ def mouseDoubleClickEvent(self, event):
+ index = self.currentIndex()
+ model = index.model()
+ status_index = model.index(index.row(), 2, index.parent())
+ status_data = status_index.data().toString()
+ hostname_index = model.index(index.row(), 1, index.parent())
+ hostname_data = hostname_index.data().toString()
+
+ if status_data == node_status['in']:
+ model.setData(status_index, QString(node_status['remove']))
+ elif status_data == node_status['out']:
+ model.setData(status_index, QString(node_status['add']))
+ elif status_data in (node_status['add'], node_status['remove']):
+ if hostname_data in already_in_nodes: model.setData(status_index, QString(node_status['in']))
+ else: model.setData(status_index, QString(node_status['out']))
+
+ model.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), hostname_index, hostname_index)
+
+ def currentChanged(self, current, previous):
+ model = current.model()
+ hostname_index = model.index(current.row(), 1, current.parent())
+ hostname_data = hostname_index.data().toString()
+ self.emit(SIGNAL('hostnameClicked(QString)'), hostname_data)
+
+
+
+class NodeNameDelegate(QStyledItemDelegate):
+ def __init__(self, parent):
+ QStyledItemDelegate.__init__(self, parent)
+
+ def paint(self, painter, option, index):
+ model = index.model()
+ data = "%s" % index.data().toString()
+ status_index = model.index(index.row(), 2, index.parent())
+ status_data = status_index.data().toString()
+
+ if status_data not in (node_status['in'], node_status['remove'], node_status['add']):
+ # default view
+ QStyledItemDelegate.paint(self, painter, option, index)
+ return
+
+ fm = QFontMetrics(option.font)
+ rect = option.rect
+ rect.setWidth(fm.width(QString(data)) + 8)
+ rect.setHeight(rect.height() - 2)
+ rect.setX(rect.x() + 4)
+ x, y, h, w = rect.x(), rect.y(), rect.height(), rect.width()
+
+ path = QPainterPath()
+ path.addRoundedRect(x, y, w, h, 4, 4)
+
+ painter.save()
+ painter.setRenderHint(QPainter.Antialiasing)
+ painter.drawRoundedRect(rect, 4, 4)
+
+ if status_data == node_status['in']: # already in the slice
+ painter.fillPath(path, QColor.fromRgb(0, 250, 0))
+ painter.setPen(QColor.fromRgb(0, 0, 0))
+ painter.drawText(option.rect, 0, QString(data))
+
+ elif status_data == node_status['add']: # newly added to the slice
+ painter.fillPath(path, QColor.fromRgb(0, 250, 0))
+ painter.setPen(QColor.fromRgb(0, 0, 0))
+ painter.drawText(option.rect, 0, QString(data))
+ painter.drawRect(x + w + 10, y + 3, 10, 10)
+ painter.fillRect(x + w + 10, y + 3, 10, 10, QColor.fromRgb(0, 250, 0))
+
+ elif status_data == node_status['remove']: # removed from the slice
+ painter.fillPath(path, QColor.fromRgb(250, 0, 0))
+ painter.setPen(QColor.fromRgb(0, 0, 0))
+ painter.drawText(option.rect, 0, QString(data))
+ painter.drawRect(x + w + 10, y + 3, 10, 10)
+ painter.fillRect(x + w + 10, y + 3, 10, 10, QColor.fromRgb(250, 0, 0))
+
+ painter.restore()
+
+
+class TreeItem:
+ def __init__(self, data, parent=None):
+ self.parentItem = parent
+ self.itemData = data
+ self.childItems = []
+
+ def clear(self):
+ for child in self.childItems:
+ child.clear()
+ del child
+ del self.childItems
+ self.childItems = []
+
+ def allChildItems(self):
+ all = []
+ for c in self.childItems:
+ all.append(c)
+ if c.childItems:
+ for cc in c.childItems:
+ all.append(cc)
+ return all
+
+ def appendChild(self, child):
+ self.childItems.append(child)
+
+ def child(self, row):
+ return self.childItems[row]
+
+ def childCount(self):
+ return len(self.childItems)
+
+ def childNumber(self):
+ if self.parentItem:
+ return self.parentItem.childItems.index(self)
+ return 0
+
+ def columnCount(self):
+ return len(self.itemData)
+
+ def data(self, column):
+ return self.itemData[column]
+
+ def insertChildren(self, position, count, columns):
+ if position < 0 or position > len(self.childItems):
+ return False
+
+ for row in range(count):
+ data = self.data(columns)
+ item = TreeItem(data, self)
+ self.childItems.insert(position, item)
+
+ return True
+
+ def insertColumns(self, position, columns):
+ if position < 0 or position > len(self.itemData):
+ return False
+
+ for column in range(columns):
+ self.itemData.insert(position, QVariant())
+
+ for child in self.childItems:
+ child.insertColumns(position, columns)
+
+ return True
+
+ def setData(self, column, value):
+ if column < 0 or column >= len(self.itemData):
+ return False
+
+ self.itemData[column] = value
+ return True
+
+ def parent(self):
+ return self.parentItem
+
+
+class NodeModel(QAbstractItemModel):
+ def __init__(self, parent):
+ QAbstractItemModel.__init__(self, parent)
+ self.__initRoot()
+
+ def clear(self):
+ self.rootItem.clear()
+ self.__initRoot()
+
+ def __initRoot(self):
+ self.rootItem = TreeItem([QString("Testbed"), QString("Hostname"), QString("Status")])
+
+ def getItem(self, index):
+ if index.isValid():
+ item = index.internalPointer()
+ if isinstance(item, TreeItem):
+ return item
+ return self.rootItem
+
+ def headerData(self, section, orientation, role):
+ if orientation == Qt.Horizontal and role in (Qt.DisplayRole, Qt.EditRole):
+ return self.rootItem.data(section)
+ return QVariant()
+
+ def index(self, row, column, parent):
+ if not self.hasIndex(row, column, parent):
+ return QModelIndex()
+
+ parentItem = self.getItem(parent)
+ childItem = parentItem.child(row)
+ if childItem:
+ return self.createIndex(row, column, childItem)
+ else:
+ return QModelIndex()
+
+ def insertColumns(self, position, columns, parent):
+ self.beginInsertColumns(parent, position, position + columns -1)
+ ret = self.rootItem.insertColumns(position, columns)
+ self.endInsertColumns()
+ return ret
+
+ def insertRows(self, position, rows, parent):
+ parentItem = self.getItem(parent)
+ self.beginInsertRows(parent, position, position + rows -1)
+ ret = parentItem.insertChildren(position, rows, self.rootItem.columnCount())
+ self.endInsertRows()
+ return ret
+
+ def parent(self, index):
+ if not index.isValid():
+ return QModelIndex()
+
+ childItem = self.getItem(index)
+ parentItem = childItem.parent()
+ if parentItem is self.rootItem:
+ return QModelIndex()
+
+ return self.createIndex(parentItem.childNumber(), 0, parentItem)
+
+ def rowCount(self, parent=QModelIndex()):
+ parentItem = self.getItem(parent)
+ return parentItem.childCount()
+
+ def columnCount(self, parent=None):
+ return self.rootItem.columnCount()
+
+ def data(self, index, role):
+ if not index.isValid():
+ return QVariant()
+
+ if role != Qt.DisplayRole and role != Qt.EditRole:
+ return QVariant()
+
+ item = self.getItem(index)
+ return item.data(index.column())
+
+ def flags(self, index):
+ if not index.isValid():
+ return 0
+ return Qt.ItemIsEnabled | Qt.ItemIsSelectable# | Qt.ItemIsEditable
+
+ def setData(self, index, value, role):
+ if role != Qt.EditRole:
+ return False
+
+ item = self.getItem(index)
+ ret = item.setData(index.column(), value)
+ if ret:
+ self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), index, index)
+ return ret
+