Example #1
0
class ListModel(QAbstractTableModel):
    orderChanged = pyqtSignal()
    elementSelected = pyqtSignal(int)


    class ColumnID():
        '''
        Define how many column the model holds and their type

        '''
        ncols=2
        Name=0
        Delete=1

    def __init__(self, elements=None, parent=None):
        '''
        Common interface for the labelListModel and the boxListModel
        see concrete implementations for details

        :param elements:
        :param parent:
        '''
        QAbstractTableModel.__init__(self, parent)

        if elements is None:
            elements = []
        self._elements = list(elements)
        self._selectionModel = QItemSelectionModel(self)

        def onSelectionChanged(selected, deselected):


            if selected:
                ind = selected[0].indexes()
                if len(ind)>0:
                    self.elementSelected.emit(ind[0].row())

        self._selectionModel.selectionChanged.connect(onSelectionChanged)

        self._allowRemove = True
        self._toolTipSuffixes = {}

        self.unremovable_rows=[] #rows in this list cannot be removed from the gui,
                                 # to add to this list call self.makeRowPermanent(int)
                                 # to remove make the self.makeRowRemovable(int)
    def makeRowPermanent(self,rowindex):
        """
        The rowindex cannot be removed from gui
        to remove this index use self.makeRowRemovable
        """

        self.unremovable_rows.append(rowindex)

    def makeRowRemovable(self,rowindex):
        self.unremovable_rows.pop(rowindex)

    def __len__(self):
        return len(self._elements)

    def __getitem__(self, i):
        return self._elements[i]

    def selectedRow(self):
        selected = self._selectionModel.selectedRows()
        if len(selected) == 1:
            return selected[0].row()
        return -1

    def selectedIndex(self):
        row = self.selectedRow()
        if row >= 0:
            return self.index(self.selectedRow())
        else:
            return QModelIndex()

    def rowCount(self, parent=None):
        return len(self._elements)

    def columnCount(self, parent):
        return self.ColumnID.ncols

    def _getToolTipSuffix(self, row):
        """
        Get the middle column tooltip suffix
        """
        suffix = "; Click to select"
        if row in self._toolTipSuffixes:
            suffix = self._toolTipSuffixes[row]
        return suffix

    def _setToolTipSuffix(self, row, text):
        """
        Set the middle column tooltip suffix
        """
        self._toolTipSuffixes[row] = text
        index = self.createIndex(row, 1)
        self.dataChanged.emit(index, index)

    class EntryToolTipAdapter(object):
        """This class can be used to make each row look like a
        separate widget with its own tooltip.

        In this case, the "tooltip" is the suffix appended to the
        tooltip of the middle column.

        """
        def __init__(self, table, row):
            self._row = row
            self._table = table
        def toolTip(self):
            return self._table._getToolTipSuffix(self._row)
        def setToolTip(self, text):
            self._table._setToolTipSuffix(self._row, text)



    def insertRow(self, position, object, parent=QModelIndex()):
        self.beginInsertRows(parent, position, position)
        object.changed.connect(self.modelReset)
        self._elements.insert(position, object)
        self.endInsertRows()
        return True

    def removeRow(self, position, parent=QModelIndex()):
        if position in self.unremovable_rows:
            return False

        self.beginRemoveRows(parent, position, position)
        value = self._elements[position]
        logger.debug("removing row: " + str(value))
        self._elements.remove(value)
        self.endRemoveRows()
        return True

    def allowRemove(self, check):
        #Allow removing of rows. Needed to be able to disallow it
        #in interactive mode
        self._allowRemove = check
        self.dataChanged.emit(self.createIndex(0, self.ColumnID.Delete),
                              self.createIndex(self.rowCount(), self.ColumnID.Delete))
    def data(self, index, role):
        '''
        Reimplement, see labelListModel or boxListModel for concrete example
        :param index:
        :param role:
        '''

        if role == Qt.EditRole and index.column() == self.ColumnID.Name:
            return self._elements[index.row()].name


        elif role == Qt.ToolTipRole and index.column() == self.ColumnID.Delete:
            return "Delete {}".format(self._elements[index.row()].name)

        elif role == Qt.ToolTipRole and index.column() == self.ColumnID.Name:
            suffix = self._getToolTipSuffix(index.row())
            return "{}\nDouble click to rename {}".format(
                self._elements[index.row()].name, suffix)
        elif role == Qt.DisplayRole and index.column() == self.ColumnID.Name:
            row = index.row()
            value = self._elements[row]
            return value.name


        if role == Qt.DecorationRole and index.column() == self.ColumnID.Delete:
            if index.row() in self.unremovable_rows: return

            row = index.row()
            pixmap = QPixmap(_NPIXELS, _NPIXELS)
            pixmap.fill(Qt.transparent)
            painter = QPainter()
            painter.begin(pixmap)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setBrush(QColor("red"))
            painter.drawEllipse(1, 1, _NPIXELS - 2, _NPIXELS - 2)
            pen = QPen(QColor("black"))
            pen.setWidth(2)
            painter.setPen(pen)

            x = _XSTART
            y = _NPIXELS - x
            painter.drawLine(x, x, y, y)
            painter.drawLine(y, x, x, y)

            painter.end()
            icon = QIcon(pixmap)
            return icon

    def flags(self, index):
        '''
        Reimplement, see labelListModel or boxListModel for concrete example
        :param index:
        '''
        if index.column() == self.ColumnID.Delete:
            if self._allowRemove:
                return Qt.ItemIsEnabled | Qt.ItemIsSelectable
            else:
                return Qt.NoItemFlags
        elif  index.column() == self.ColumnID.Name:
            return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable

    def setData(self, index, value, role=Qt.EditRole):
        '''
        Reimplement, see labelListModel or boxListModel for concrete example
        :param index:
        '''
        if role == Qt.EditRole  and index.column() == self.ColumnID.Name:
            row = index.row()
            name = value
            self._elements[row].name = str(name.toString())
            self.dataChanged.emit(index, index)
            return True

        return False

    def select(self, row):
        '''
        Reimplement, see labelListModel or boxListModel for concrete example
        :param row
        '''
        self._selectionModel.clear()
        self._selectionModel.select(self.index(row, self.ColumnID.Name),
                                    QItemSelectionModel.Select)

    def clearSelectionModel(self):
        self._selectionModel.clear()
Example #2
0
class LayerStackModel(QAbstractListModel):
    canMoveSelectedUp = pyqtSignal("bool")
    canMoveSelectedDown = pyqtSignal("bool")
    canDeleteSelected = pyqtSignal("bool")

    orderChanged = pyqtSignal()
    layerAdded = pyqtSignal(Layer, int)  # is now in row
    layerRemoved = pyqtSignal(Layer, int)  # was in row
    stackCleared = pyqtSignal()

    def __init__(self, parent=None):
        QAbstractListModel.__init__(self, parent)
        self._layerStack = []
        self.selectionModel = QItemSelectionModel(self)
        self.selectionModel.selectionChanged.connect(self.updateGUI)
        self.selectionModel.selectionChanged.connect(self._onSelectionChanged)
        self._movingRows = False
        QTimer.singleShot(0, self.updateGUI)

        def _handleRemovedLayer(layer):
            # Layerstacks *own* the layers they hold, and thus are
            #  responsible for cleaning them up when they are removed:
            layer.clean_up()

        self.layerRemoved.connect(_handleRemovedLayer)

    ####
    ## High level API to manipulate the layerstack
    ###

    def __len__(self):
        return self.rowCount()

    def __repr__(self):
        return "<LayerStackModel: layerStack='%r'>" % (self._layerStack, )

    def __getitem__(self, i):
        return self._layerStack[i]

    def __iter__(self):
        return self._layerStack.__iter__()

    def layerIndex(self, layer):
        #note that the 'index' function already has a different implementation
        #from Qt side
        return self._layerStack.index(layer)

    def findMatchingIndex(self, func):
        """Call the given function with each layer and return the index of the first layer for which f is True."""
        for index, layer in enumerate(self._layerStack):
            if func(layer):
                return index
        raise ValueError("No matching layer in stack.")

    def append(self, data):
        self.insert(0, data)

    def clear(self):
        if len(self) > 0:
            self.removeRows(0, len(self))
            self.stackCleared.emit()

    def insert(self, index, data):
        """
        Insert a layer into this layer stack, which *takes ownership* of the layer.
        """
        assert isinstance(
            data, Layer), "Only Layers can be added to a LayerStackModel"
        self.insertRow(index)
        self.setData(self.index(index), data)
        if self.selectedRow() >= 0:
            self.selectionModel.select(self.index(self.selectedRow()),
                                       QItemSelectionModel.Deselect)
        self.selectionModel.select(self.index(index),
                                   QItemSelectionModel.Select)

        data.changed.connect(
            functools.partial(self._onLayerChanged, self.index(index)))
        index = self._layerStack.index(data)
        self.layerAdded.emit(data, index)

        self.updateGUI()

    def selectRow(self, row):
        already_selected_rows = self.selectionModel.selectedRows()
        if len(already_selected_rows) == 1 and row == already_selected_rows[0]:
            # Nothing to do if this row is already selected
            return
        self.selectionModel.clear()
        self.selectionModel.setCurrentIndex(self.index(row),
                                            QItemSelectionModel.SelectCurrent)

    @pyqtSignature("deleteSelected()")
    def deleteSelected(self):
        num_rows = len(self.selectionModel.selectedRows())
        assert num_rows == 1, "Can't delete selected row: {} layers are currently selected.".format(
            num_rows)
        row = self.selectionModel.selectedRows()[0]
        layer = self._layerStack[row.row()]
        assert not layer._cleaned_up, "This layer ({}) has already been cleaned up.  Shouldn't it already be removed from the layerstack?".format(
            layer.name)
        self.removeRow(row.row())
        if self.rowCount() > 0:
            self.selectionModel.select(self.index(0),
                                       QItemSelectionModel.Select)
        self.layerRemoved.emit(layer, row.row())
        self.updateGUI()

    @pyqtSignature("moveSelectedUp()")
    def moveSelectedUp(self):
        assert len(self.selectionModel.selectedRows()) == 1
        row = self.selectionModel.selectedRows()[0]
        if row.row() != 0:
            oldRow = row.row()
            newRow = oldRow - 1
            self._moveToRow(oldRow, newRow)

    @pyqtSignature("moveSelectedDown()")
    def moveSelectedDown(self):
        assert len(self.selectionModel.selectedRows()) == 1
        row = self.selectionModel.selectedRows()[0]
        if row.row() != self.rowCount() - 1:
            oldRow = row.row()
            newRow = oldRow + 1
            self._moveToRow(oldRow, newRow)

    @pyqtSignature("moveSelectedToTop()")
    def moveSelectedToTop(self):
        assert len(self.selectionModel.selectedRows()) == 1
        row = self.selectionModel.selectedRows()[0]
        if row.row() != 0:
            oldRow = row.row()
            newRow = 0
            self._moveToRow(oldRow, newRow)

    @pyqtSignature("moveSelectedToBottom()")
    def moveSelectedToBottom(self):
        assert len(self.selectionModel.selectedRows()) == 1
        row = self.selectionModel.selectedRows()[0]
        if row.row() != self.rowCount() - 1:
            oldRow = row.row()
            newRow = self.rowCount() - 1
            self._moveToRow(oldRow, newRow)

    def moveSelectedToRow(self, newRow):
        assert len(self.selectionModel.selectedRows()) == 1
        row = self.selectionModel.selectedRows()[0]
        if row.row() != newRow:
            oldRow = row.row()
            self._moveToRow(oldRow, newRow)

    def _moveToRow(self, oldRow, newRow):
        d = self._layerStack[oldRow]
        self.removeRow(oldRow)
        self.insertRow(newRow)
        self.setData(self.index(newRow), d)
        self.selectionModel.select(self.index(newRow),
                                   QItemSelectionModel.Select)
        self.orderChanged.emit()
        self.updateGUI()

    ####
    ## Low level API. To add, remove etc. layers use the high level API from above.
    ####

    def updateGUI(self):
        self.canMoveSelectedUp.emit(self.selectedRow() > 0)
        self.canMoveSelectedDown.emit(self.selectedRow() < self.rowCount() - 1)
        self.canDeleteSelected.emit(self.rowCount() > 0)
        self.wantsUpdate()

    def selectedRow(self):
        selected = self.selectionModel.selectedRows()
        if len(selected) == 1:
            return selected[0].row()
        return -1

    def selectedIndex(self):
        row = self.selectedRow()
        if row >= 0:
            return self.index(self.selectedRow())
        else:
            return QModelIndex()

    def rowCount(self, parent=QModelIndex()):
        if not parent.isValid():
            return len(self._layerStack)
        return 0

    def insertRows(self, row, count, parent=QModelIndex()):
        '''Insert empty rows in the stack. 
        
        DO NOT USE THIS METHOD TO INSERT NEW LAYERS!
        Always use the insert() or append() method.
        
        '''
        if parent.isValid():
            return False
        oldRowCount = self.rowCount()
        #for some reason, row can be negative!
        beginRow = max(0, row)
        endRow = min(beginRow + count - 1, len(self._layerStack))
        self.beginInsertRows(parent, beginRow, endRow)
        while (beginRow <= endRow):
            self._layerStack.insert(row, Layer(datasources=[]))
            beginRow += 1
        self.endInsertRows()
        assert self.rowCount(
        ) == oldRowCount + 1, "oldRowCount = %d, self.rowCount() = %d" % (
            oldRowCount, self.rowCount())
        return True

    def removeRows(self, row, count, parent=QModelIndex()):
        '''Remove rows from the stack. 
        
        DO NOT USE THIS METHOD TO REMOVE LAYERS!
        Use the deleteSelected() method instead.
        
        '''

        if parent.isValid():
            return False
        if row + count <= 0 or row >= len(self._layerStack):
            return False
        oldRowCount = self.rowCount()
        beginRow = max(0, row)
        endRow = min(row + count - 1, len(self._layerStack) - 1)
        self.beginRemoveRows(parent, beginRow, endRow)
        while (beginRow <= endRow):
            del self._layerStack[row]
            beginRow += 1
        self.endRemoveRows()
        return True

    def flags(self, index):
        defaultFlags = Qt.ItemIsSelectable | Qt.ItemIsEditable | Qt.ItemIsEnabled
        if index.isValid():
            return Qt.ItemIsDragEnabled | defaultFlags
        else:
            return Qt.ItemIsDropEnabled | defaultFlags

    def supportedDropActions(self):
        return Qt.MoveAction

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return None
        if index.row() > len(self._layerStack):
            return None

        if role == Qt.DisplayRole or role == Qt.EditRole:
            return self._layerStack[index.row()]
        elif role == Qt.ToolTipRole:
            return self._layerStack[index.row()].toolTip()
        else:
            return None

    def setData(self, index, value, role=Qt.EditRole):
        '''Replace one layer with another. 
        
        DO NOT USE THIS METHOD TO INSERT NEW LAYERS!
        Use deleteSelected() followed by insert() or append().
        
        '''
        if role == Qt.EditRole:
            layer = value
            if not isinstance(value, Layer):
                layer = value.toPyObject()
            self._layerStack[index.row()] = layer
            self.dataChanged.emit(index, index)
            return True
        elif role == Qt.ToolTipRole:
            self._layerStack[index.row()].setToolTip()
            return True
        return False

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role != Qt.DisplayRole:
            return None
        if orientation == Qt.Horizontal:
            return "Column %r" % section
        else:
            return "Row %r" % section

    def wantsUpdate(self):
        self.layoutChanged.emit()

    def _onLayerChanged(self, idx):
        self.dataChanged.emit(idx, idx)
        self.updateGUI()

    def _onSelectionChanged(self, selected, deselected):
        for idx in deselected.indexes():
            self[idx.row()].setActive(False)
        for idx in selected.indexes():
            self[idx.row()].setActive(True)
Example #3
0
class LabelListModel(QAbstractTableModel):
    orderChanged = pyqtSignal()
    labelSelected = pyqtSignal(int)
    
    def __init__(self, labels = [], parent = None):
        QAbstractTableModel.__init__(self, parent)
        self._labels = labels
        self._selectionModel = QItemSelectionModel(self)
        
        def onSelectionChanged(selected, deselected):
            if selected:
                self.labelSelected.emit(selected[0].indexes()[0].row())
        self._selectionModel.selectionChanged.connect(onSelectionChanged)
        
        self._allowRemove = True
    
    def __len__(self):
        return len(self._labels)
    
    def __getitem__(self, i):
        return self._labels[i]
     
    def selectedRow(self):
        selected = self._selectionModel.selectedRows()
        if len(selected) == 1:
            return selected[0].row()
        return -1
    
    def selectedIndex(self):
        row = self.selectedRow()
        if row >= 0:
            return self.index(self.selectedRow())
        else:
            return QModelIndex()
            
    def rowCount(self, parent=None):
        return len(self._labels)
    
    def columnCount(self, parent):
        return 3

    def data(self, index, role):
        if role == Qt.EditRole and index.column() == 0:
            return self._labels[index.row()].color
        if role == Qt.EditRole and index.column() == 1:
            return self._labels[index.row()].name
        
        if role == Qt.ToolTipRole and index.column() == 0:
            return "Hex code : " + self._labels[index.row()].color.name() + "\n DoubleClick to change"
        if role == Qt.ToolTipRole and index.column() == 1:
            return self._labels[index.row()].name + "\n DoubleClick to rename"
        if role == Qt.ToolTipRole and index.column() == 2:
            return "Delete " + self._labels[index.row()].name
        
        if role == Qt.DecorationRole and index.column() == 0:
            row = index.row()
            value = self._labels[row]
            pixmap = QPixmap(26, 26)
            pixmap.fill(value.color)
            icon = QIcon(pixmap)
            return icon
        
        if role == Qt.DecorationRole and index.column() == 2:
            row = index.row()
            pixmap = QPixmap(26, 26)
            pixmap.fill(Qt.transparent)
            painter = QPainter()
            painter.begin(pixmap)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setBrush(QColor("red"))
            painter.drawEllipse(1, 1, 24, 24)
            pen = QPen(QColor("black"))
            pen.setWidth(2)
            painter.setPen(pen)
            painter.drawLine(8,8, 18,18)
            painter.drawLine(18,8, 8,18)
            
            painter.end()
            icon = QIcon(pixmap)
            return icon
        
        if role == Qt.DisplayRole and index.column() == 1:
            row = index.row()
            value = self._labels[row]
            return value.name

    def flags(self, index):
        if  index.column() == 0:
            return Qt.ItemIsEnabled | Qt.ItemIsSelectable
        elif  index.column() == 1:
            return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable
        elif  index.column() == 2:
            if self._allowRemove:
                return Qt.ItemIsEnabled | Qt.ItemIsSelectable
            else:
                return Qt.NoItemFlags
        
    def setData(self, index, value, role = Qt.EditRole):
        if role == Qt.EditRole  and index.column() == 0:
            row = index.row()
            color = QColor(value)
            if color.isValid():
                self._labels[row].color = color
                self.dataChanged.emit(index, index)
                return True
            
        if role == Qt.EditRole  and index.column() == 1:
            row = index.row()
            name = value
            self._labels[row].name = str(name.toString())
            self.dataChanged.emit(index, index)
            return True
        return False

    def insertRow(self, position, object, parent = QModelIndex()):
        self.beginInsertRows(parent, position, position)
        self._labels.insert(position, object)
        self.endInsertRows()
        return True

    def removeRow(self, position, parent = QModelIndex()):
        self.beginRemoveRows(parent, position, position)
        value = self._labels[position]
        print "removing row: ", value
        self._labels.remove(value)     
        self.endRemoveRows()
        return True
    
    def allowRemove(self, check):
        #Allow removing of rows. Needed to be able to disallow it
        #in interactive mode
        self._allowRemove = check
        self.dataChanged.emit(self.createIndex(0, 2), self.createIndex(self.rowCount(), 2))

    def select(self, row):
        self._selectionModel.clear()
        self._selectionModel.select(self.index(row, 0), QItemSelectionModel.Select)
        self._selectionModel.select(self.index(row, 1), QItemSelectionModel.Select)
class LabelListModel(QAbstractTableModel):
    orderChanged = pyqtSignal()
    labelSelected = pyqtSignal(int)

    def __init__(self, labels=[], parent=None):
        QAbstractTableModel.__init__(self, parent)
        self._labels = labels
        self._selectionModel = QItemSelectionModel(self)

        def onSelectionChanged(selected, deselected):
            if selected:
                self.labelSelected.emit(selected[0].indexes()[0].row())

        self._selectionModel.selectionChanged.connect(onSelectionChanged)

        self._allowRemove = True
        self._toolTipSuffixes = {}

    def __len__(self):
        return len(self._labels)

    def __getitem__(self, i):
        return self._labels[i]

    def selectedRow(self):
        selected = self._selectionModel.selectedRows()
        if len(selected) == 1:
            return selected[0].row()
        return -1

    def selectedIndex(self):
        row = self.selectedRow()
        if row >= 0:
            return self.index(self.selectedRow())
        else:
            return QModelIndex()

    def rowCount(self, parent=None):
        return len(self._labels)

    def columnCount(self, parent):
        return 3

    def _getToolTipSuffix(self, row):
        """
        Get the middle column tooltip suffix
        """
        suffix = "; Click to select"
        if row in self._toolTipSuffixes:
            suffix = self._toolTipSuffixes[row]
        return suffix

    def _setToolTipSuffix(self, row, text):
        """
        Set the middle column tooltip suffix
        """
        self._toolTipSuffixes[row] = text
        index = self.createIndex(row, 1)
        self.dataChanged.emit(index, index)

    class EntryToolTipAdapter(object):
        """
        This class can be used to make each row look like a separate widget with its own tooltip.
        In this case, the "tooltip" is the suffix appended to the tooltip of the middle column.
        """
        def __init__(self, table, row):
            self._row = row
            self._table = table

        def toolTip(self):
            return self._table._getToolTipSuffix(self._row)

        def setToolTip(self, text):
            self._table._setToolTipSuffix(self._row, text)

    def data(self, index, role):
        if role == Qt.EditRole and index.column() == 0:
            return self._labels[index.row()].color
        if role == Qt.EditRole and index.column() == 1:
            return self._labels[index.row()].name

        if role == Qt.ToolTipRole and index.column() == 0:
            return "Hex code : " + self._labels[
                index.row()].color.name() + "\n DoubleClick to change"
        if role == Qt.ToolTipRole and index.column() == 1:
            suffix = self._getToolTipSuffix(index.row())
            return self._labels[
                index.row()].name + "\n DoubleClick to rename" + suffix
        if role == Qt.ToolTipRole and index.column() == 2:
            return "Delete " + self._labels[index.row()].name

        if role == Qt.DecorationRole and index.column() == 0:
            row = index.row()
            value = self._labels[row]
            pixmap = QPixmap(26, 26)
            pixmap.fill(value.color)
            icon = QIcon(pixmap)
            return icon

        if role == Qt.DecorationRole and index.column() == 2:
            row = index.row()
            pixmap = QPixmap(26, 26)
            pixmap.fill(Qt.transparent)
            painter = QPainter()
            painter.begin(pixmap)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setBrush(QColor("red"))
            painter.drawEllipse(1, 1, 24, 24)
            pen = QPen(QColor("black"))
            pen.setWidth(2)
            painter.setPen(pen)
            painter.drawLine(8, 8, 18, 18)
            painter.drawLine(18, 8, 8, 18)

            painter.end()
            icon = QIcon(pixmap)
            return icon

        if role == Qt.DisplayRole and index.column() == 1:
            row = index.row()
            value = self._labels[row]
            return value.name

    def flags(self, index):
        if index.column() == 0:
            return Qt.ItemIsEnabled | Qt.ItemIsSelectable
        elif index.column() == 1:
            return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable
        elif index.column() == 2:
            if self._allowRemove:
                return Qt.ItemIsEnabled | Qt.ItemIsSelectable
            else:
                return Qt.NoItemFlags

    def setData(self, index, value, role=Qt.EditRole):
        if role == Qt.EditRole and index.column() == 0:
            row = index.row()
            color = QColor(value)
            if color.isValid():
                self._labels[row].color = color
                self.dataChanged.emit(index, index)
                return True

        if role == Qt.EditRole and index.column() == 1:
            row = index.row()
            name = value
            self._labels[row].name = str(name.toString())
            self.dataChanged.emit(index, index)
            return True

        return False

    def insertRow(self, position, object, parent=QModelIndex()):
        self.beginInsertRows(parent, position, position)
        self._labels.insert(position, object)
        self.endInsertRows()
        return True

    def removeRow(self, position, parent=QModelIndex()):
        self.beginRemoveRows(parent, position, position)
        value = self._labels[position]
        logger.debug("removing row: " + str(value))
        self._labels.remove(value)
        self.endRemoveRows()
        return True

    def allowRemove(self, check):
        #Allow removing of rows. Needed to be able to disallow it
        #in interactive mode
        self._allowRemove = check
        self.dataChanged.emit(self.createIndex(0, 2),
                              self.createIndex(self.rowCount(), 2))

    def select(self, row):
        self._selectionModel.clear()
        self._selectionModel.select(self.index(row, 0),
                                    QItemSelectionModel.Select)
        self._selectionModel.select(self.index(row, 1),
                                    QItemSelectionModel.Select)
Example #5
0
class ListModel(QAbstractTableModel):
    orderChanged = pyqtSignal()
    elementSelected = pyqtSignal(int)


    class ColumnID():
        '''
        Define how many column the model holds and their type

        '''
        ncols=2
        Name=0
        Delete=1

    def __init__(self, elements=None, parent=None):
        '''
        Common interface for the labelListModel and the boxListModel
        see concrete implementations for details

        :param elements:
        :param parent:
        '''
        QAbstractTableModel.__init__(self, parent)

        if elements is None:
            elements = []
        self._elements = list(elements)
        self._selectionModel = QItemSelectionModel(self)

        def onSelectionChanged(selected, deselected):


            if selected:
                ind = selected[0].indexes()
                if len(ind)>0:
                    self.elementSelected.emit(ind[0].row())

        self._selectionModel.selectionChanged.connect(onSelectionChanged)

        self._allowRemove = True
        self._toolTipSuffixes = {}

        self.unremovable_rows=[] #rows in this list cannot be removed from the gui,
                                 # to add to this list call self.makeRowPermanent(int)
                                 # to remove make the self.makeRowRemovable(int)
    def makeRowPermanent(self,rowindex):
        """
        The rowindex cannot be removed from gui
        to remove this index use self.makeRowRemovable
        """

        self.unremovable_rows.append(rowindex)

    def makeRowRemovable(self,rowindex):
        self.unremovable_rows.pop(rowindex)

    def __len__(self):
        return len(self._elements)

    def __getitem__(self, i):
        return self._elements[i]

    def selectedRow(self):
        selected = self._selectionModel.selectedRows()
        if len(selected) == 1:
            return selected[0].row()
        return -1

    def selectedIndex(self):
        row = self.selectedRow()
        if row >= 0:
            return self.index(self.selectedRow())
        else:
            return QModelIndex()

    def rowCount(self, parent=None):
        return len(self._elements)

    def columnCount(self, parent):
        return self.ColumnID.ncols

    def _getToolTipSuffix(self, row):
        """
        Get the middle column tooltip suffix
        """
        suffix = "; Click to select"
        if row in self._toolTipSuffixes:
            suffix = self._toolTipSuffixes[row]
        return suffix

    def _setToolTipSuffix(self, row, text):
        """
        Set the middle column tooltip suffix
        """
        self._toolTipSuffixes[row] = text
        index = self.createIndex(row, 1)
        self.dataChanged.emit(index, index)

    class EntryToolTipAdapter(object):
        """This class can be used to make each row look like a
        separate widget with its own tooltip.

        In this case, the "tooltip" is the suffix appended to the
        tooltip of the middle column.

        """
        def __init__(self, table, row):
            self._row = row
            self._table = table
        def toolTip(self):
            return self._table._getToolTipSuffix(self._row)
        def setToolTip(self, text):
            self._table._setToolTipSuffix(self._row, text)



    def insertRow(self, position, object, parent=QModelIndex()):
        self.beginInsertRows(parent, position, position)
        object.changed.connect(self.modelReset)
        self._elements.insert(position, object)
        self.endInsertRows()
        return True

    def removeRow(self, position, parent=QModelIndex()):
        if position in self.unremovable_rows:
            return False

        self.beginRemoveRows(parent, position, position)
        value = self._elements[position]
        logger.debug("removing row: " + str(value))
        self._elements.remove(value)
        self.endRemoveRows()
        return True

    def allowRemove(self, check):
        #Allow removing of rows. Needed to be able to disallow it
        #in interactive mode
        self._allowRemove = check
        self.dataChanged.emit(self.createIndex(0, self.ColumnID.Delete),
                              self.createIndex(self.rowCount(), self.ColumnID.Delete))
    def data(self, index, role):
        '''
        Reimplement, see labelListModel or boxListModel for concrete example
        :param index:
        :param role:
        '''

        if role == Qt.EditRole and index.column() == self.ColumnID.Name:
            name = self._elements[index.row()].name
            return decode_to_qstring(name, 'utf-8')

        elif role == Qt.ToolTipRole and index.column() == self.ColumnID.Delete:
            s = "Delete {}".format(self._elements[index.row()].name)
            return decode_to_qstring(s, 'utf-8')

        elif role == Qt.ToolTipRole and index.column() == self.ColumnID.Name:
            suffix = self._getToolTipSuffix(index.row())
            s = "{}\nDouble click to rename {}".format(
                self._elements[index.row()].name, suffix)
            return decode_to_qstring(s, 'utf-8')
        elif role == Qt.DisplayRole and index.column() == self.ColumnID.Name:
            name = self._elements[index.row()].name
            return decode_to_qstring(name, 'utf-8')

        if role == Qt.DecorationRole and index.column() == self.ColumnID.Delete:
            if index.row() in self.unremovable_rows: return

            row = index.row()
            pixmap = QPixmap(_NPIXELS, _NPIXELS)
            pixmap.fill(Qt.transparent)
            painter = QPainter()
            painter.begin(pixmap)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setBrush(QColor("red"))
            painter.drawEllipse(1, 1, _NPIXELS - 2, _NPIXELS - 2)
            pen = QPen(QColor("black"))
            pen.setWidth(2)
            painter.setPen(pen)

            x = _XSTART
            y = _NPIXELS - x
            painter.drawLine(x, x, y, y)
            painter.drawLine(y, x, x, y)

            painter.end()
            icon = QIcon(pixmap)
            return icon

    def flags(self, index):
        '''
        Reimplement, see labelListModel or boxListModel for concrete example
        :param index:
        '''
        if index.column() == self.ColumnID.Delete:
            if self._allowRemove:
                return Qt.ItemIsEnabled | Qt.ItemIsSelectable
            else:
                return Qt.NoItemFlags
        elif  index.column() == self.ColumnID.Name:
            return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable

    def setData(self, index, value, role=Qt.EditRole):
        '''
        Reimplement, see labelListModel or boxListModel for concrete example
        :param index:
        '''
        if role == Qt.EditRole  and index.column() == self.ColumnID.Name:
            row = index.row()
            # value is a user provided QVariant, possibly with unicode
            # characters in it. internally, we keep a str
            self._elements[row].name = encode_from_qstring(value.toString(), 'utf-8')
            self.dataChanged.emit(index, index)
            return True

        return False

    def select(self, row):
        '''
        Reimplement, see labelListModel or boxListModel for concrete example
        :param row
        '''
        self._selectionModel.clear()
        self._selectionModel.select(self.index(row, self.ColumnID.Name),
                                    QItemSelectionModel.Select)

    def clearSelectionModel(self):
        self._selectionModel.clear()
Example #6
0
class LabelListModel(QAbstractTableModel):
    orderChanged = pyqtSignal()
    labelSelected = pyqtSignal(int)

    def __init__(self, labels=None, parent=None):
        QAbstractTableModel.__init__(self, parent)

        if labels is None:
            labels = []
        self._labels = list(labels)
        self._selectionModel = QItemSelectionModel(self)

        def onSelectionChanged(selected, deselected):
            if selected:
                self.labelSelected.emit(selected[0].indexes()[0].row())

        self._selectionModel.selectionChanged.connect(onSelectionChanged)

        self._allowRemove = True
        self._toolTipSuffixes = {}

    def __len__(self):
        return len(self._labels)

    def __getitem__(self, i):
        return self._labels[i]

    def selectedRow(self):
        selected = self._selectionModel.selectedRows()
        if len(selected) == 1:
            return selected[0].row()
        return -1

    def selectedIndex(self):
        row = self.selectedRow()
        if row >= 0:
            return self.index(self.selectedRow())
        else:
            return QModelIndex()

    def rowCount(self, parent=None):
        return len(self._labels)

    def columnCount(self, parent):
        return 3

    def _getToolTipSuffix(self, row):
        """
        Get the middle column tooltip suffix
        """
        suffix = "; Click to select"
        if row in self._toolTipSuffixes:
            suffix = self._toolTipSuffixes[row]
        return suffix

    def _setToolTipSuffix(self, row, text):
        """
        Set the middle column tooltip suffix
        """
        self._toolTipSuffixes[row] = text
        index = self.createIndex(row, 1)
        self.dataChanged.emit(index, index)

    class EntryToolTipAdapter(object):
        """This class can be used to make each row look like a
        separate widget with its own tooltip.

        In this case, the "tooltip" is the suffix appended to the
        tooltip of the middle column.

        """
        def __init__(self, table, row):
            self._row = row
            self._table = table
        def toolTip(self):
            return self._table._getToolTipSuffix(self._row)
        def setToolTip(self, text):
            self._table._setToolTipSuffix(self._row, text)

    def data(self, index, role):
        if role == Qt.EditRole and index.column() == ColumnID.Color:
            return (self._labels[index.row()].brushColor(),
                    self._labels[index.row()].pmapColor())

        if role == Qt.EditRole and index.column() == ColumnID.Name:
            return self._labels[index.row()].name

        if role == Qt.ToolTipRole and index.column() == ColumnID.Color:
            return ("Hex code : {}\nDouble click to change".format(
                self._labels[index.row()].brushColor().name()))

        if role == Qt.ToolTipRole and index.column() == ColumnID.Name:
            suffix = self._getToolTipSuffix(index.row())
            return "{}\nDouble click to rename {}".format(
                self._labels[index.row()].name, suffix)

        if role == Qt.ToolTipRole and index.column() == ColumnID.Delete:
            return "Delete {}".format(self._labels[index.row()].name)

        if role == Qt.DecorationRole and index.column() == ColumnID.Color:
            row = index.row()
            value = self._labels[row]
            if value.brushColor == value.pmapColor():
                pixmap = QPixmap(_NPIXELS, _NPIXELS)
                pixmap.fill(value.brushColor)
            else:
                a = value.brushColor().rgba()
                b = value.pmapColor().rgba()
                img = QImage(_NPIXELS,_NPIXELS, QImage.Format_RGB32)
                for i in range(_NPIXELS):
                    for j in range(0, _NPIXELS - i):
                        img.setPixel(i, j, a)
                for i in range(_NPIXELS):
                    for j in range(_NPIXELS - i, _NPIXELS):
                        img.setPixel(i, j, b)
                pixmap = QPixmap.fromImage(img)
            icon = QIcon(pixmap)
            return icon

        if role == Qt.DecorationRole and index.column() == ColumnID.Delete:
            row = index.row()
            pixmap = QPixmap(_NPIXELS, _NPIXELS)
            pixmap.fill(Qt.transparent)
            painter = QPainter()
            painter.begin(pixmap)
            painter.setRenderHint(QPainter.Antialiasing)
            painter.setBrush(QColor("red"))
            painter.drawEllipse(1, 1, _NPIXELS - 2, _NPIXELS - 2)
            pen = QPen(QColor("black"))
            pen.setWidth(2)
            painter.setPen(pen)

            x = _XSTART
            y = _NPIXELS - x
            painter.drawLine(x, x, y, y)
            painter.drawLine(y, x, x, y)

            painter.end()
            icon = QIcon(pixmap)
            return icon

        if role == Qt.DisplayRole and index.column() == ColumnID.Name:
            row = index.row()
            value = self._labels[row]
            return value.name

    def flags(self, index):
        if  index.column() == ColumnID.Color:
            return Qt.ItemIsEnabled | Qt.ItemIsSelectable
        elif  index.column() == ColumnID.Name:
            return Qt.ItemIsEditable | Qt.ItemIsEnabled | Qt.ItemIsSelectable
        elif  index.column() == ColumnID.Delete:
            if self._allowRemove:
                return Qt.ItemIsEnabled | Qt.ItemIsSelectable
            else:
                return Qt.NoItemFlags

    def setData(self, index, value, role=Qt.EditRole):
        if role == Qt.EditRole  and index.column() == ColumnID.Color:
            row = index.row()
            brushColor = QColor(value[0])
            pmapColor = QColor(value[1])
            if brushColor.isValid() and pmapColor.isValid():
                print "setData: brushColor = {}, pmapColor = {}".format(
                    brushColor.name(), pmapColor.name())
                print "  self._labels[row] has type {}".format(
                    type(self._labels[row]))
                self._labels[row].setBrushColor(brushColor)
                self._labels[row].setPmapColor(pmapColor)
                print "  self._labels[row].brushColor = {}".format(
                    self._labels[row].brushColor().name())
                print "  self._labels[row].pmapColor  = {}".format(
                    self._labels[row].pmapColor().name())
                self.dataChanged.emit(index, index)
                return True

        if role == Qt.EditRole  and index.column() == ColumnID.Name:
            row = index.row()
            name = value
            self._labels[row].name = str(name.toString())
            self.dataChanged.emit(index, index)
            return True

        return False

    def insertRow(self, position, object, parent=QModelIndex()):
        self.beginInsertRows(parent, position, position)
        self._labels.insert(position, object)
        self.endInsertRows()
        return True

    def removeRow(self, position, parent=QModelIndex()):
        self.beginRemoveRows(parent, position, position)
        value = self._labels[position]
        logger.debug("removing row: " + str(value))
        self._labels.remove(value)
        self.endRemoveRows()
        return True

    def allowRemove(self, check):
        #Allow removing of rows. Needed to be able to disallow it
        #in interactive mode
        self._allowRemove = check
        self.dataChanged.emit(self.createIndex(0, ColumnID.Delete),
                              self.createIndex(self.rowCount(), ColumnID.Delete))

    def select(self, row):
        self._selectionModel.clear()
        self._selectionModel.select(self.index(row, ColumnID.Color),
                                    QItemSelectionModel.Select)
        self._selectionModel.select(self.index(row, ColumnID.Name),
                                    QItemSelectionModel.Select)