Exemple #1
0
class OntologyExplorerWidget(QtWidgets.QWidget):
    """
    This class implements the ontology explorer used to list ontology predicates.
    """
    sgnItemActivated = QtCore.pyqtSignal('QGraphicsItem')
    sgnItemClicked = QtCore.pyqtSignal('QGraphicsItem')
    sgnItemDoubleClicked = QtCore.pyqtSignal('QGraphicsItem')
    sgnItemRightClicked = QtCore.pyqtSignal('QGraphicsItem')

    sgnIRIItemActivated = QtCore.pyqtSignal(IRI)
    sgnIRIItemClicked = QtCore.pyqtSignal(IRI)
    sgnIRIItemDoubleClicked = QtCore.pyqtSignal(IRI)
    sgnIRIItemRightClicked = QtCore.pyqtSignal(IRI)

    def __init__(self, plugin):
        """
        Initialize the ontology explorer widget.
        :type plugin: Session
        """
        super().__init__(plugin.session)

        self.plugin = plugin
        self.items = [
            Item.ConceptIRINode, Item.RoleIRINode, Item.AttributeIRINode,
            Item.IndividualIRINode, Item.ValueDomainIRINode
        ]
        self.unsatisfiableItems = list()
        self.unsatisfiableClasses = list()
        self.unsatisfiableObjProps = list()
        self.unsatisfiableDataProps = list()

        self.iconAttribute = QtGui.QIcon(':/icons/18/ic_treeview_attribute')
        self.iconConcept = QtGui.QIcon(':/icons/18/ic_treeview_concept')
        self.iconInstance = QtGui.QIcon(':/icons/18/ic_treeview_instance')
        self.iconRole = QtGui.QIcon(':/icons/18/ic_treeview_role')
        self.iconValue = QtGui.QIcon(':/icons/18/ic_treeview_value')

        self.searchShortcut = QtWidgets.QShortcut(QtGui.QKeySequence('Ctrl+f'),
                                                  self.session)
        self.search = StringField(self)
        self.search.setAcceptDrops(False)
        self.search.setClearButtonEnabled(True)
        self.search.setPlaceholderText('Search...')
        self.search.setToolTip('Search ({})'.format(
            self.searchShortcut.key().toString(QtGui.QKeySequence.NativeText)))
        self.search.setFixedHeight(30)
        self.model = QtGui.QStandardItemModel(self)
        self.proxy = OntologyExplorerFilterProxyModel(self)
        self.proxy.setDynamicSortFilter(False)
        self.proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.proxy.setSortCaseSensitivity(QtCore.Qt.CaseSensitive)
        self.proxy.setSourceModel(self.model)
        self.ontoview = OntologyExplorerView(self)
        self.ontoview.setModel(self.proxy)
        self.mainLayout = QtWidgets.QVBoxLayout(self)
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.addWidget(self.search)
        self.mainLayout.addWidget(self.ontoview)
        self.setTabOrder(self.search, self.ontoview)
        self.setContentsMargins(0, 0, 0, 0)
        self.setMinimumWidth(216)
        self.setStyleSheet("""
            QLineEdit,
            QLineEdit:editable,
            QLineEdit:hover,
            QLineEdit:pressed,
            QLineEdit:focus {
              border: none;
              border-radius: 0;
              background: #FFFFFF;
              color: #000000;
              padding: 4px 4px 4px 4px;
            }
        """)
        header = self.ontoview.header()
        header.setStretchLastSection(False)
        header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)

        connect(self.ontoview.activated, self.onItemActivated)
        connect(self.ontoview.doubleClicked, self.onItemDoubleClicked)
        connect(self.ontoview.pressed, self.onItemPressed)
        connect(self.search.textChanged, self.doFilterItem)
        connect(self.search.returnPressed, self.onReturnPressed)
        connect(self.searchShortcut.activated, self.doFocusSearch)
        connect(self.sgnItemActivated, self.session.doFocusItem)
        connect(self.sgnItemDoubleClicked, self.session.doFocusItem)
        connect(self.sgnItemRightClicked, self.session.doFocusItem)

        connect(self.session.sgnPrefixAdded, self.onPrefixAdded)
        connect(self.session.sgnPrefixRemoved, self.onPrefixRemoved)
        connect(self.session.sgnPrefixModified, self.onPrefixModified)
        connect(self.session.sgnRenderingModified, self.onRenderingModified)

        connect(self.session.sgnIRIRemovedFromAllDiagrams,
                self.onIRIRemovedFromAllDiagrams)
        connect(self.session.sgnSingleNodeSwitchIRI,
                self.onSingleNodeIRISwitched)

    #############################################
    #   PROPERTIES
    #################################

    @property
    def project(self):
        """
        Returns the reference to the active project.
        :rtype: Session
        """
        return self.session.project

    @property
    def session(self):
        """
        Returns the reference to the active session.
        :rtype: Session
        """
        return self.plugin.parent()

    #############################################
    #   EVENTS
    #################################

    def paintEvent(self, paintEvent):
        """
        This is needed for the widget to pick the stylesheet.
        :type paintEvent: QPaintEvent
        """
        option = QtWidgets.QStyleOption()
        option.initFrom(self)
        painter = QtGui.QPainter(self)
        style = self.style()
        style.drawPrimitive(QtWidgets.QStyle.PE_Widget, option, painter, self)

    #############################################
    #   SLOTS
    #################################

    @QtCore.pyqtSlot(str)
    def onRenderingModified(self, rendering):
        # RESET MODEL DISPLAY DATA
        # Here we force te explorer view to become invisible while we update
        # its model in order to avoid unnecessary UI updates that could
        # otherwise cause the entire application to become unresponsive.
        # This is kind of a hack but it works for the moment.
        self.ontoview.setVisible(False)
        for index in range(self.model.rowCount()):
            item = self.model.item(index)
            data = item.data(OntologyExplorerView.IRIRole)
            if isinstance(data, IRI):
                item.setText(self.parentKeyForIRI(data))
        self.model.dataChanged.emit(
            self.model.index(0, 0),
            self.model.index(self.model.rowCount() - 1, 0))
        self.ontoview.setVisible(True)
        # APPLY FILTERS AND SORT
        self.proxy.invalidateFilter()
        self.proxy.sort(0, QtCore.Qt.AscendingOrder)

    @QtCore.pyqtSlot(str, str)
    def onPrefixAdded(self, _prefix: str, _ns: str):
        settings = QtCore.QSettings()
        rendering = settings.value('ontology/iri/render',
                                   IRIRender.PREFIX.value, str)
        if rendering == IRIRender.PREFIX.value or rendering == IRIRender.LABEL.value:
            self.redrawIRIItem()

    @QtCore.pyqtSlot(str)
    def onPrefixRemoved(self, _: str):
        settings = QtCore.QSettings()
        rendering = settings.value('ontology/iri/render',
                                   IRIRender.PREFIX.value, str)
        if rendering == IRIRender.PREFIX.value or rendering == IRIRender.LABEL.value:
            self.redrawIRIItem()

    @QtCore.pyqtSlot(str)
    def onPrefixModified(self, _: str):
        settings = QtCore.QSettings()
        rendering = settings.value('ontology/iri/render',
                                   IRIRender.PREFIX.value, str)
        if rendering == IRIRender.PREFIX.value or rendering == IRIRender.LABEL.value:
            self.redrawIRIItem()

    @QtCore.pyqtSlot(str)
    def onIRIModified(self, _: str):
        iri = self.sender()
        self.redrawIRIItem(iri)

    @QtCore.pyqtSlot(AnnotationAssertion)
    def onIRIAnnotationAssertionAdded(self, _):
        iri = self.sender()
        settings = QtCore.QSettings()
        rendering = settings.value('ontology/iri/render',
                                   IRIRender.PREFIX.value, str)
        if rendering == IRIRender.PREFIX.value or rendering == IRIRender.LABEL.value:
            self.redrawIRIItem(iri)

    @QtCore.pyqtSlot(AnnotationAssertion)
    def onIRIAnnotationAssertionRemoved(self, _):
        iri = self.sender()
        settings = QtCore.QSettings()
        rendering = settings.value('ontology/iri/render',
                                   IRIRender.PREFIX.value, str)
        if rendering == IRIRender.PREFIX.value or rendering == IRIRender.LABEL.value:
            self.redrawIRIItem(iri)

    @QtCore.pyqtSlot(AnnotationAssertion)
    def onIRIAnnotationAssertionModified(self, _):
        iri = self.sender()
        settings = QtCore.QSettings()
        rendering = settings.value('ontology/iri/render',
                                   IRIRender.PREFIX.value, str)
        if rendering == IRIRender.PREFIX.value or rendering == IRIRender.LABEL.value:
            self.redrawIRIItem(iri)

    @QtCore.pyqtSlot()
    def onNodeIRISwitched(self):
        node = self.sender()
        self.doAddNode(node.diagram, node)

    @QtCore.pyqtSlot(AbstractNode, IRI)
    def onSingleNodeIRISwitched(self, node, oldIRI):
        oldParentK = self.parentKeyForIRI(oldIRI)
        for parent in self.model.findItems(oldParentK, QtCore.Qt.MatchExactly):
            rowCount = parent.rowCount()
            for i in range(rowCount):
                child = parent.child(i)
                if child.data(OntologyExplorerView.IRIRole) is node:
                    parent.removeRow(i)
                    break
            if not parent.rowCount():
                if isinstance(node, (
                        OntologyEntityNode,
                        OntologyEntityResizableNode,
                )):
                    self.disconnectIRISignals(
                        parent.data(OntologyExplorerView.IRIRole))
                self.model.removeRow(parent.index().row())

    @QtCore.pyqtSlot(IRI)
    def onIRIRemovedFromAllDiagrams(self, iri):
        parentK = self.parentKeyForIRI(iri)
        for parent in self.model.findItems(parentK, QtCore.Qt.MatchExactly):
            '''
            removeParent = True
            rowCount = parent.rowCount()
            for i in range(rowCount):
                childData = parent.child(i).data(QtCore.Qt.UserRole)
                if isinstance(childData,OntologyEntityNode) or isinstance(childData, OntologyEntityResizableNode):
                    parent.removeRow(i)
                else:
                    removeParent = False
            if removeParent:
                self.model.removeRow(parent.index().row())
            '''
            self.model.removeRow(parent.index().row())

    @QtCore.pyqtSlot(IRI)
    def onUnsatisfiableClass(self, iri):
        parent = self.parentForIRI(iri)
        if parent:
            parent.setData(OntologyExplorerView.UnsatisfiableBrush,
                           QtCore.Qt.ForegroundRole)
            self.unsatisfiableItems.append(parent)
            self.unsatisfiableClasses.append(parent)

    @QtCore.pyqtSlot(IRI)
    def onUnsatisfiableObjectProperty(self, iri):
        parent = self.parentForIRI(iri)
        if parent:
            parent.setData(OntologyExplorerView.UnsatisfiableBrush,
                           QtCore.Qt.ForegroundRole)
            self.unsatisfiableItems.append(parent)
            self.unsatisfiableObjProps.append(parent)

    @QtCore.pyqtSlot(IRI)
    def onUnsatisfiableDataProperty(self, iri):
        parent = self.parentForIRI(iri)
        if parent:
            parent.setData(OntologyExplorerView.UnsatisfiableBrush,
                           QtCore.Qt.ForegroundRole)
            self.unsatisfiableItems.append(parent)
            self.unsatisfiableDataProps.append(parent)

    @QtCore.pyqtSlot()
    def doResetReasonerHighlight(self):
        for item in self.unsatisfiableItems:
            item.setData(None, QtCore.Qt.ForegroundRole)
        self.unsatisfiableItems = list()
        self.unsatisfiableClasses = list()
        self.unsatisfiableObjProps = list()
        self.unsatisfiableDataProps = list()

    @QtCore.pyqtSlot(ImportedOntology)
    def onImportedOntologyAdded(self, impOnt):
        """
        :param impOnt:ImportedOntology
        :return:
        """
        for classIRI in impOnt.classes:
            parent = self.parentForIRI(classIRI)
            if not parent:
                parent = QtGui.QStandardItem(self.parentKeyForIRI(classIRI))
                parent.setData(classIRI, OntologyExplorerView.IRIRole)
                self.connectIRISignals(classIRI)
                self.model.appendRow(parent)
            child = QtGui.QStandardItem(
                self.childKeyForImported(impOnt, classIRI))
            # CHECK FOR DUPLICATE NODES
            children = [parent.child(i) for i in range(parent.rowCount())]
            if not any([(child.text() == c.text()
                         and c.icon() is self.iconConcept) for c in children]):
                child.setIcon(self.iconConcept)
                childData = [classIRI, Item.ConceptIRINode.value]
                child.setData(childData, OntologyExplorerView.IRIRole)
                parent.appendRow(child)
        for objPropIRI in impOnt.objectProperties:
            parent = self.parentForIRI(objPropIRI)
            if not parent:
                parent = QtGui.QStandardItem(self.parentKeyForIRI(objPropIRI))
                parent.setData(objPropIRI, OntologyExplorerView.IRIRole)
                self.connectIRISignals(objPropIRI)
                self.model.appendRow(parent)
            child = QtGui.QStandardItem(
                self.childKeyForImported(impOnt, objPropIRI))
            # CHECK FOR DUPLICATE NODES
            children = [parent.child(i) for i in range(parent.rowCount())]
            if not any([(child.text() == c.text()
                         and c.icon() is self.iconRole) for c in children]):
                child.setIcon(self.iconRole)
                childData = [objPropIRI, Item.RoleIRINode.value]
                child.setData(childData, OntologyExplorerView.IRIRole)
                parent.appendRow(child)
        for dataPropIRI in impOnt.dataProperties:
            parent = self.parentForIRI(dataPropIRI)
            if not parent:
                parent = QtGui.QStandardItem(self.parentKeyForIRI(dataPropIRI))
                parent.setData(dataPropIRI, OntologyExplorerView.IRIRole)
                self.connectIRISignals(dataPropIRI)
                self.model.appendRow(parent)
            child = QtGui.QStandardItem(
                self.childKeyForImported(impOnt, dataPropIRI))
            # CHECK FOR DUPLICATE NODES
            children = [parent.child(i) for i in range(parent.rowCount())]
            if not any([(child.text() == c.text()
                         and c.icon() is self.iconAttribute)
                        for c in children]):
                child.setIcon(self.iconAttribute)
                childData = [dataPropIRI, Item.AttributeIRINode.value]
                child.setData(childData, OntologyExplorerView.IRIRole)
                parent.appendRow(child)
        for indIRI in impOnt.individuals:
            parent = self.parentForIRI(indIRI)
            if not parent:
                parent = QtGui.QStandardItem(self.parentKeyForIRI(indIRI))
                parent.setData(indIRI, OntologyExplorerView.IRIRole)
                self.connectIRISignals(indIRI)
                self.model.appendRow(parent)
            child = QtGui.QStandardItem(
                self.childKeyForImported(impOnt, indIRI))
            # CHECK FOR DUPLICATE NODES
            children = [parent.child(i) for i in range(parent.rowCount())]
            if not any([(child.text() == c.text()
                         and c.icon() is self.iconInstance)
                        for c in children]):
                child.setIcon(self.iconInstance)
                childData = [indIRI, Item.IndividualIRINode.value]
                child.setData(childData, OntologyExplorerView.IRIRole)
                parent.appendRow(child)
        # APPLY FILTERS AND SORT
        if self.sender() != self.plugin:
            self.proxy.invalidateFilter()
            self.proxy.sort(0, QtCore.Qt.AscendingOrder)

    @QtCore.pyqtSlot(ImportedOntology)
    def onImportedOntologyRemoved(self, impOnt):
        """
        :param impOnt:ImportedOntology
        :return:
        """
        for classIRI in impOnt.classes:
            parent = self.parentForIRI(classIRI)
            if parent:
                child = self.childForImported(parent, impOnt, classIRI)
                if child:
                    parent.removeRow((child.index().row()))
                if not parent.rowCount():
                    self.disconnectIRISignals(classIRI)
                    self.model.removeRow(parent.index().row())
        for objPropIRI in impOnt.objectProperties:
            parent = self.parentForIRI(objPropIRI)
            if parent:
                child = self.childForImported(parent, impOnt, objPropIRI)
                if child:
                    parent.removeRow((child.index().row()))
                if not parent.rowCount():
                    self.disconnectIRISignals(objPropIRI)
                    self.model.removeRow(parent.index().row())
        for dataPropIRI in impOnt.dataProperties:
            parent = self.parentForIRI(dataPropIRI)
            if parent:
                child = self.childForImported(parent, impOnt, dataPropIRI)
                if child:
                    parent.removeRow((child.index().row()))
                if not parent.rowCount():
                    self.disconnectIRISignals(dataPropIRI)
                    self.model.removeRow(parent.index().row())
        for indIRI in impOnt.individuals:
            parent = self.parentForIRI(indIRI)
            if parent:
                child = self.childForImported(parent, impOnt, indIRI)
                if child:
                    parent.removeRow((child.index().row()))
                if not parent.rowCount():
                    self.disconnectIRISignals(indIRI)
                    self.model.removeRow(parent.index().row())
        # APPLY FILTERS AND SORT
        if self.sender() != self.plugin:
            self.proxy.invalidateFilter()
            self.proxy.sort(0, QtCore.Qt.AscendingOrder)

    @QtCore.pyqtSlot('QGraphicsScene', 'QGraphicsItem')
    def doAddNode(self, diagram, node):
        """
        Add a node in the tree view.
        :type diagram: QGraphicsScene
        :type node: AbstractItem
        """
        if node.type() in self.items:
            parent = self.parentFor(node)
            if not parent:
                if not isinstance(node, (
                        OntologyEntityNode,
                        OntologyEntityResizableNode,
                )):
                    parent = QtGui.QStandardItem(self.parentKey(node))
                    parent.setIcon(self.iconFor(node))
                else:
                    parent = QtGui.QStandardItem(self.parentKeyForIRI(
                        node.iri))
                    parent.setData(node.iri, OntologyExplorerView.IRIRole)
                    self.connectIRISignals(node.iri)
                self.model.appendRow(parent)
            child = QtGui.QStandardItem(self.childKey(diagram, node))
            if isinstance(node, (
                    OntologyEntityNode,
                    OntologyEntityResizableNode,
            )):
                child.setIcon(self.iconFor(node))
                connect(node.sgnIRISwitched, self.onNodeIRISwitched)
            child.setData(node, OntologyExplorerView.IRIRole)
            parent.appendRow(child)
            # APPLY FILTERS AND SORT
            if self.sender() != self.plugin:
                self.proxy.invalidateFilter()
                self.proxy.sort(0, QtCore.Qt.AscendingOrder)

    @QtCore.pyqtSlot('QGraphicsScene', 'QGraphicsItem')
    def doRemoveNode(self, diagram, node):
        """
        Remove a node from the tree view.
        :type diagram: QGraphicsScene
        :type node: AbstractItem
        """
        if node.type() in self.items:
            parent = self.parentFor(node)
            if parent:
                child = self.childFor(parent, diagram, node)
                if child:
                    parent.removeRow(child.index().row())
                if not parent.rowCount():
                    if isinstance(node, (
                            OntologyEntityNode,
                            OntologyEntityResizableNode,
                    )):
                        self.disconnectIRISignals(
                            parent.data(OntologyExplorerView.IRIRole))
                    self.model.removeRow(parent.index().row())

    @QtCore.pyqtSlot(str)
    def doFilterItem(self, key):
        """
        Executed when the search box is filled with data.
        :type key: str
        """
        self.proxy.setFilterFixedString(key)
        self.proxy.sort(QtCore.Qt.AscendingOrder)

    @QtCore.pyqtSlot()
    def doFocusSearch(self):
        """
        Focus the search bar.
        """
        # RAISE THE ENTIRE WIDGET TREE IF IT IS NOT VISIBLE
        if not self.isVisible():
            widget = self
            while widget != self.session:
                widget.show()
                widget.raise_()
                widget = widget.parent()
        self.search.setFocus()
        self.search.selectAll()

    @QtCore.pyqtSlot('QModelIndex')
    def onItemActivated(self, index):
        """
        Executed when an item in the treeview is activated (e.g. by pressing Return or Enter key).
        :type index: QModelIndex
        """
        # noinspection PyArgumentList
        if QtWidgets.QApplication.mouseButtons() == QtCore.Qt.NoButton:
            item = self.model.itemFromIndex(self.proxy.mapToSource(index))
            if item and item.data(OntologyExplorerView.IRIRole):
                if isinstance(item.data(OntologyExplorerView.IRIRole), IRI):
                    self.sgnIRIItemActivated.emit(
                        item.data(OntologyExplorerView.IRIRole))
                else:
                    self.sgnItemActivated.emit(
                        item.data(OntologyExplorerView.IRIRole))
                # KEEP FOCUS ON THE TREE VIEW UNLESS SHIFT IS PRESSED
                if QtWidgets.QApplication.queryKeyboardModifiers(
                ) & QtCore.Qt.SHIFT:
                    return
                self.ontoview.setFocus()
            elif item:
                # EXPAND/COLLAPSE PARENT ITEM
                if self.ontoview.isExpanded(index):
                    self.ontoview.collapse(index)
                else:
                    self.ontoview.expand(index)

    @QtCore.pyqtSlot('QModelIndex')
    def onItemDoubleClicked(self, index):
        """
        Executed when an item in the treeview is double clicked.
        :type index: QModelIndex
        """
        # noinspection PyArgumentList
        if QtWidgets.QApplication.mouseButtons() & QtCore.Qt.LeftButton:
            item = self.model.itemFromIndex(self.proxy.mapToSource(index))
            if item and item.data(OntologyExplorerView.IRIRole):
                if isinstance(item.data(OntologyExplorerView.IRIRole), IRI):
                    self.sgnIRIItemDoubleClicked.emit(
                        item.data(OntologyExplorerView.IRIRole))
                elif isinstance(item.data(OntologyExplorerView.IRIRole),
                                AbstractNode):
                    self.sgnItemDoubleClicked.emit(
                        item.data(OntologyExplorerView.IRIRole))

    @QtCore.pyqtSlot('QModelIndex')
    def onItemPressed(self, index):
        """
        Executed when an item in the treeview is clicked.
        :type index: QModelIndex
        """
        # noinspection PyArgumentList
        if QtWidgets.QApplication.mouseButtons() & QtCore.Qt.LeftButton:
            item = self.model.itemFromIndex(self.proxy.mapToSource(index))
            if item and item.data(OntologyExplorerView.IRIRole):
                if isinstance(item.data(OntologyExplorerView.IRIRole), IRI):
                    self.sgnIRIItemClicked.emit(
                        item.data(OntologyExplorerView.IRIRole))
                elif isinstance(item.data(OntologyExplorerView.IRIRole),
                                AbstractNode):
                    self.sgnItemClicked.emit(
                        item.data(OntologyExplorerView.IRIRole))

    @QtCore.pyqtSlot()
    def onReturnPressed(self):
        """
        Executed when the Return or Enter key is pressed in the search field.
        """
        self.focusNextChild()

    #############################################
    #   INTERFACE
    #################################

    def connectNodeSignals(self, node):
        """
        :type node: OntologyEntityNode | OntologyEntityResizableNode
        """
        connect(node.sgnIRISwitched, self.onNodeIRISwitched)

    def connectIRISignals(self, iri):
        """
        :type iri: IRI
        """
        connect(iri.sgnAnnotationAdded, self.onIRIAnnotationAssertionAdded)
        connect(iri.sgnAnnotationRemoved, self.onIRIAnnotationAssertionRemoved)
        connect(iri.sgnAnnotationModified,
                self.onIRIAnnotationAssertionModified)
        connect(iri.sgnIRIModified, self.onIRIModified)

    def disconnectIRISignals(self, iri):
        """
        :type iri: IRI
        """
        disconnect(iri.sgnAnnotationAdded, self.onIRIAnnotationAssertionAdded)
        disconnect(iri.sgnAnnotationRemoved,
                   self.onIRIAnnotationAssertionRemoved)
        disconnect(iri.sgnAnnotationModified,
                   self.onIRIAnnotationAssertionModified)
        disconnect(iri.sgnIRIModified, self.onIRIModified)

    def redrawIRIItem(self, iri=None):
        self.ontoview.setSortingEnabled(False)
        for row in range(self.model.rowCount()):
            currItem = self.model.item(row)
            if iri:
                currIRI = currItem.data(OntologyExplorerView.IRIRole)
                if currIRI is iri:
                    currItem.setText(self.parentKeyForIRI(iri))
                    break
            else:
                if isinstance(currItem.data(OntologyExplorerView.IRIRole),
                              IRI):
                    currItem.setText(
                        self.parentKeyForIRI(
                            currItem.data(OntologyExplorerView.IRIRole)))
        self.ontoview.setSortingEnabled(True)
        if self.sender() != self.plugin:
            self.proxy.invalidateFilter()
            self.proxy.sort(0, QtCore.Qt.AscendingOrder)

    def childFor(self, parent, diagram, node):
        """
        Search the item representing this node among parent children.
        :type parent: QtGui.QStandardItem
        :type diagram: Diagram
        :type node: AbstractNode
        """
        key = self.childKey(diagram, node)
        for i in range(parent.rowCount()):
            child = parent.child(i)
            if child.text() == key:
                return child
        return None

    def childForImported(self, parent, impOnt, iri):
        """
        Search the item representing this node among parent children.
        :type parent: QtGui.QStandardItem
        :type impOnt: ImportedOntology
        :type iri: IRI
        """
        key = self.childKeyForImported(impOnt, iri)
        for i in range(parent.rowCount()):
            child = parent.child(i)
            if child.text() == key:
                return child
        return None

    @staticmethod
    def childKey(diagram, node):
        """
        Returns the child key (text) used to place the given node in the treeview.
        :type diagram: Diagram
        :type node: AbstractNode
        :rtype: str
        """
        diagram = rstrip(diagram.name, File.Graphol.extension)

        if isinstance(node, (OntologyEntityNode, OntologyEntityResizableNode)):
            return '{0} - {1}'.format(diagram, node.id)
        else:
            predicate = node.text().replace('\n', '')
            return '{0} ({1} - {2})'.format(predicate, diagram, node.id)

    @staticmethod
    def childKeyForImported(impOnt, iri):
        """
        Returns the child key (text) used to place the given node in the treeview.
        :type impOnt: ImportedOntology
        :type iri: IRI
        :rtype: str
        """
        return 'Imported from {}'.format(impOnt.docLocation)

    def iconFor(self, node):
        """
        Returns the icon for the given node.
        :type node:
        """
        if node.type() is Item.AttributeIRINode:
            return self.iconAttribute
        if node.type() is Item.ConceptIRINode:
            return self.iconConcept
        if node.type() is Item.IndividualIRINode:
            return self.iconInstance
        if node.type() is Item.RoleIRINode:
            return self.iconRole
        if node.type() is Item.ValueDomainIRINode:
            return self.iconValue

    def parentFor(self, node):
        """
        Search the parent element of the given node.
        :type node: AbstractNode
        :rtype: QtGui.QStandardItem
        """
        if isinstance(node, (
                OntologyEntityNode,
                OntologyEntityResizableNode,
        )):
            parentK = self.parentKeyForIRI(node.iri)
            for i in self.model.findItems(parentK, QtCore.Qt.MatchExactly):
                parentIRI = i.data(OntologyExplorerView.IRIRole)
                if node.iri is parentIRI:
                    return i
        else:
            parentK = self.parentKey(node)
            for i in self.model.findItems(parentK, QtCore.Qt.MatchExactly):
                n = i.child(0).data(OntologyExplorerView.IRIRole)
                if node.type() is n.type():
                    return i
        return None

    def parentForIRI(self, iri):
        """
        Search the parent element of the given iri.
        :type node: IRI
        :rtype: QtGui.QStandardItem
        """
        parentK = self.parentKeyForIRI(iri)
        for i in self.model.findItems(parentK, QtCore.Qt.MatchExactly):
            parentIRI = i.data(OntologyExplorerView.IRIRole)
            if iri is parentIRI:
                return i
        return None

    @staticmethod
    def parentKeyForIRI(iri):
        return IRIRender.iriLabelString(iri).replace('\n', '')

    @staticmethod
    def parentKey(node):
        """
        Returns the parent key (text) used to place the given node in the treeview.
        :type node: AbstractNode
        :type project Project
        :rtype: str
        """
        return node.text().replace('\n', '')

    def sizeHint(self):
        """
        Returns the recommended size for this widget.
        :rtype: QtCore.QSize
        """
        return QtCore.QSize(216, 266)
Exemple #2
0
class TableExplorerWidget(QtWidgets.QWidget):
    """
    This class implements the schema explorer used to list schema tables.
    """
    sgnGraphicalNodeItemClicked = QtCore.pyqtSignal('QGraphicsItem')
    sgnGraphicalNodeItemActivated = QtCore.pyqtSignal('QGraphicsItem')
    sgnGraphicalNodeItemDoubleClicked = QtCore.pyqtSignal('QGraphicsItem')
    sgnGraphicalNodeItemRightClicked = QtCore.pyqtSignal('QGraphicsItem')
    sgnRelationalTableItemClicked = QtCore.pyqtSignal(RelationalTable)
    sgnRelationalTableItemActivated = QtCore.pyqtSignal(RelationalTable)
    sgnRelationalTableItemDoubleClicked = QtCore.pyqtSignal(RelationalTable)
    sgnRelationalTableItemRightClicked = QtCore.pyqtSignal(RelationalTable)

    def __init__(self, plugin):
        super().__init__(plugin.session)

        self.plugin = plugin
        self.items = [
            EntityType.Class, EntityType.ObjectProperty,
            EntityType.DataProperty
        ]
        self.classIcon = QtGui.QIcon(':/icons/18/ic_treeview_concept')
        self.objPropIcon = QtGui.QIcon(':/icons/18/ic_treeview_role')
        self.dataPropIcon = QtGui.QIcon(':/icons/18/ic_treeview_attribute')
        self.searchShortcut = QtWidgets.QShortcut(
            QtGui.QKeySequence('Ctrl+f+t'), plugin.session)
        self.search = StringField(self)
        self.search.setAcceptDrops(False)
        self.search.setClearButtonEnabled(True)
        self.search.setPlaceholderText('Search...')
        self.search.setToolTip('Search ({})'.format(
            self.searchShortcut.key().toString(QtGui.QKeySequence.NativeText)))
        self.search.setFixedHeight(30)
        self.model = QtGui.QStandardItemModel(self)
        self.proxy = TableExplorerFilterProxyModel(self)
        self.proxy.setDynamicSortFilter(False)
        self.proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.proxy.setSortCaseSensitivity(QtCore.Qt.CaseSensitive)
        self.proxy.setSourceModel(self.model)
        self.tableview = TableExplorerView(self)
        self.tableview.setModel(self.proxy)
        self.mainLayout = QtWidgets.QVBoxLayout(self)
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.addWidget(self.search)
        self.mainLayout.addWidget(self.tableview)
        self.setTabOrder(self.search, self.tableview)
        self.setContentsMargins(0, 0, 0, 0)
        self.setMinimumWidth(216)

        self.setStyleSheet("""
            QLineEdit,
            QLineEdit:editable,
            QLineEdit:hover,
            QLineEdit:pressed,
            QLineEdit:focus {
              border: none;
              border-radius: 0;
              background: #FFFFFF;
              color: #000000;
              padding: 4px 4px 4px 4px;
            }
        """)

        header = self.tableview.header()
        header.setStretchLastSection(False)
        header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)

        connect(plugin.sgnSchemaChanged, self.onSchemaChanged)
        connect(plugin.sgnNodeAdded, self.doAddNode)
        connect(self.tableview.pressed, self.onItemPressed)
        connect(self.tableview.doubleClicked, self.onItemDoubleClicked)
        connect(self.search.textChanged, self.doFilterItem)
        connect(self.search.returnPressed, self.onReturnPressed)
        connect(self.searchShortcut.activated, self.doFocusSearch)
        connect(self.sgnGraphicalNodeItemActivated, self.plugin.doFocusItem)
        connect(self.sgnGraphicalNodeItemClicked, self.plugin.doFocusItem)
        connect(self.sgnGraphicalNodeItemDoubleClicked,
                self.plugin.doFocusItem)
        connect(self.sgnGraphicalNodeItemRightClicked, self.plugin.doFocusItem)
        connect(self.sgnRelationalTableItemActivated, self.plugin.doFocusTable)
        connect(self.sgnRelationalTableItemClicked, self.plugin.doFocusTable)
        connect(self.sgnRelationalTableItemDoubleClicked,
                self.plugin.doFocusTable)
        connect(self.sgnRelationalTableItemRightClicked,
                self.plugin.doFocusTable)

    #############################################
    #   SLOTS
    #################################

    @QtCore.pyqtSlot(RelationalSchema)
    def onSchemaChanged(self, schema):
        """
        Add a node in the tree view.
        :type schema: RelationalSchema
        """
        self.model.clear()

    @QtCore.pyqtSlot(BlackBirdDiagram, TableNode)
    def doAddNode(self, diagram, node):
        """
        Add a node in the tree view.
        :type diagram: QGraphicsScene
        :type node: TableNode
        """
        parent = self.parentFor(node)
        if not parent:
            parent = QtGui.QStandardItem(self.parentKey(node))
            parent.setIcon(self.iconFor(node.relationalTable))
            parent.setData(node.relationalTable)
            self.model.appendRow(parent)
        child = QtGui.QStandardItem(self.childKey(diagram, node))
        child.setData(node)
        parent.appendRow(child)
        # APPLY FILTERS AND SORT
        if self.sender() != self.plugin:
            self.proxy.invalidateFilter()
            self.proxy.sort(0, QtCore.Qt.AscendingOrder)
        else:
            self.doFilterItem('')

    @QtCore.pyqtSlot('QGraphicsScene', 'QGraphicsItem')
    def doRemoveNode(self, diagram, node):
        """
        Remove a node from the tree view.
        :type diagram: QGraphicsScene
        :type node: AbstractItem
        """
        pass

    @QtCore.pyqtSlot(str)
    def doFilterItem(self, key):
        """
        Executed when the search box is filled with data.
        :type key: str
        """
        self.proxy.setFilterFixedString(key)
        self.proxy.sort(QtCore.Qt.AscendingOrder)

    @QtCore.pyqtSlot()
    def doFocusSearch(self):
        """
        Focus the search bar.
        """
        # RAISE THE ENTIRE WIDGET TREE IF IT IS NOT VISIBLE
        if not self.isVisible():
            widget = self
            while widget != self.session:
                widget.show()
                widget.raise_()
                widget = widget.parent()
        self.search.setFocus()
        self.search.selectAll()

    @QtCore.pyqtSlot()
    def onReturnPressed(self):
        """
        Executed when the Return or Enter key is pressed in the search field.
        """
        self.focusNextChild()

    @QtCore.pyqtSlot('QModelIndex')
    def onItemActivated(self, index):
        """
        Executed when an item in the treeview is activated (e.g. by pressing Return or Enter key).
        :type index: QModelIndex
        """
        # noinspection PyArgumentList
        if QtWidgets.QApplication.mouseButtons() == QtCore.Qt.NoButton:
            item = self.model.itemFromIndex(self.proxy.mapToSource(index))
            if item and item.data():
                if isinstance(item.data(), RelationalTable):
                    self.sgnRelationalTableItemActivated.emit(item.data())
                elif isinstance(item.data(), TableNode):
                    # self.sgnGraphicalNodeItemActivated.emit(item.data())
                    self.sgnRelationalTableItemActivated.emit(
                        item.data().relationalTable)
                # KEEP FOCUS ON THE TREE VIEW UNLESS SHIFT IS PRESSED
                if QtWidgets.QApplication.queryKeyboardModifiers(
                ) & QtCore.Qt.SHIFT:
                    return
                self.tableview.setFocus()
            elif item:
                # EXPAND/COLLAPSE PARENT ITEM
                if self.tableview.isExpanded(index):
                    self.tableview.collapse(index)
                else:
                    self.tableview.expand(index)

    @QtCore.pyqtSlot('QModelIndex')
    def onItemDoubleClicked(self, index):
        """
        Executed when an item in the treeview is double clicked.
        :type index: QModelIndex
        """
        # noinspection PyArgumentList
        if QtWidgets.QApplication.mouseButtons() & QtCore.Qt.LeftButton:
            item = self.model.itemFromIndex(self.proxy.mapToSource(index))
            if item and item.data():
                if isinstance(item.data(), RelationalTable):
                    self.sgnRelationalTableItemDoubleClicked.emit(item.data())
                elif isinstance(item.data(), TableNode):
                    self.sgnGraphicalNodeItemDoubleClicked.emit(item.data())
                    self.sgnRelationalTableItemDoubleClicked.emit(
                        item.data().relationalTable)

    @QtCore.pyqtSlot('QModelIndex')
    def onItemPressed(self, index):
        """
        Executed when an item in the treeview is clicked.
        :type index: QModelIndex
        """
        # noinspection PyArgumentList
        if QtWidgets.QApplication.mouseButtons() & QtCore.Qt.LeftButton:
            item = self.model.itemFromIndex(self.proxy.mapToSource(index))
            if item and item.data():
                if isinstance(item.data(), RelationalTable):
                    self.sgnRelationalTableItemClicked.emit(item.data())
                elif isinstance(item.data(), TableNode):
                    # self.sgnGraphicalNodeItemClicked.emit(item.data())
                    self.sgnRelationalTableItemClicked.emit(
                        item.data().relationalTable)

    #############################################
    #   INTERFACE
    #################################

    def iconFor(self, table):
        """
        Returns the icon for the given node.
        :type table:RelationalTable
        """
        entity = table.entity
        entityType = entity.entityType
        if entityType is EntityType.Class:
            return self.classIcon
        if entityType is EntityType.ObjectProperty:
            return self.objPropIcon
        if entityType is EntityType.DataProperty:
            return self.dataPropIcon

    def parentFor(self, node):
        """
        Search the parent element of the given node.
        :type node: TableNode
        :rtype: QtGui.QStandardItem
        """
        for i in self.model.findItems(self.parentKey(node),
                                      QtCore.Qt.MatchExactly):
            if i.child(0):
                n = i.child(0).data()
                if node.type() is n.type():
                    return i
        return None

    @staticmethod
    def childKey(diagram, node):
        """
        Returns the child key (text) used to place the given node in the treeview.
        :type diagram: Diagram
        :type node: TableNode
        :rtype: str
        """
        diagram = rstrip(diagram.name, File.Graphol.extension)
        return '[{0} - {1}] ({2})'.format(diagram, node.id,
                                          node.relationalTable.name)

    @staticmethod
    def parentKey(node):
        """
        Returns the parent key (text) used to place the given node in the treeview.
        :type node: Union[TableNode,RelationalTable]
        :rtype: str
        """
        if isinstance(node, RelationalTable):
            return node.name
        if isinstance(node, TableNode):
            return node.relationalTable.name

    def sizeHint(self):
        """
        Returns the recommended size for this widget.
        :rtype: QtCore.QSize
        """
        return QtCore.QSize(216, 266)
Exemple #3
0
class OntologyExplorerWidget(QtWidgets.QWidget):
    """
    This class implements the ontology explorer used to list ontology predicates.
    """
    sgnItemActivated = QtCore.pyqtSignal('QGraphicsItem')
    sgnItemClicked = QtCore.pyqtSignal('QGraphicsItem')
    sgnItemDoubleClicked = QtCore.pyqtSignal('QGraphicsItem')
    sgnItemRightClicked = QtCore.pyqtSignal('QGraphicsItem')

    def __init__(self, plugin):
        """
        Initialize the ontology explorer widget.
        :type plugin: Session
        """
        super().__init__(plugin.session)

        self.plugin = plugin
        self.items = [
            Item.ConceptNode, Item.RoleNode, Item.AttributeNode,
            Item.IndividualNode
        ]
        self.status = [Status.DEFAULT, Status.DRAFT, Status.FINAL]

        self.iconAttribute = QtGui.QIcon(':/icons/18/ic_treeview_attribute')
        self.iconConcept = QtGui.QIcon(':/icons/18/ic_treeview_concept')
        self.iconInstance = QtGui.QIcon(':/icons/18/ic_treeview_instance')
        self.iconRole = QtGui.QIcon(':/icons/18/ic_treeview_role')
        self.iconValue = QtGui.QIcon(':/icons/18/ic_treeview_value')

        self.searchShortcut = QtWidgets.QShortcut(QtGui.QKeySequence('Ctrl+f'),
                                                  self.session)
        self.search = StringField(self)
        self.search.setAcceptDrops(False)
        self.search.setClearButtonEnabled(True)
        self.search.setPlaceholderText('Search...')
        self.search.setToolTip('Search ({})'.format(
            self.searchShortcut.key().toString(QtGui.QKeySequence.NativeText)))
        self.search.setFixedHeight(30)
        self.model = QtGui.QStandardItemModel(self)
        self.proxy = OntologyExplorerFilterProxyModel(self)
        self.proxy.setDynamicSortFilter(False)
        self.proxy.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.proxy.setSortCaseSensitivity(QtCore.Qt.CaseSensitive)
        self.proxy.setSourceModel(self.model)
        self.ontoview = OntologyExplorerView(self)
        self.ontoview.setModel(self.proxy)
        self.mainLayout = QtWidgets.QVBoxLayout(self)
        self.mainLayout.setContentsMargins(0, 0, 0, 0)
        self.mainLayout.addWidget(self.search)
        self.mainLayout.addWidget(self.ontoview)
        self.setTabOrder(self.search, self.ontoview)
        self.setContentsMargins(0, 0, 0, 0)
        self.setMinimumWidth(216)

        self.setStyleSheet("""
            QLineEdit,
            QLineEdit:editable,
            QLineEdit:hover,
            QLineEdit:pressed,
            QLineEdit:focus {
              border: none;
              border-radius: 0;
              background: #FFFFFF;
              color: #000000;
              padding: 4px 4px 4px 4px;
            }
        """)

        header = self.ontoview.header()
        header.setStretchLastSection(False)
        header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)

        connect(self.ontoview.activated, self.onItemActivated)
        connect(self.ontoview.doubleClicked, self.onItemDoubleClicked)
        connect(self.ontoview.pressed, self.onItemPressed)
        connect(self.search.textChanged, self.doFilterItem)
        connect(self.search.returnPressed, self.onReturnPressed)
        connect(self.searchShortcut.activated, self.doFocusSearch)
        connect(self.sgnItemActivated, self.session.doFocusItem)
        connect(self.sgnItemDoubleClicked, self.session.doFocusItem)
        connect(self.sgnItemRightClicked, self.session.doFocusItem)

    #############################################
    #   PROPERTIES
    #################################

    @property
    def project(self):
        """
        Returns the reference to the active project.
        :rtype: Session
        """
        return self.session.project

    @property
    def session(self):
        """
        Returns the reference to the active session.
        :rtype: Session
        """
        return self.plugin.parent()

    #############################################
    #   EVENTS
    #################################

    def paintEvent(self, paintEvent):
        """
        This is needed for the widget to pick the stylesheet.
        :type paintEvent: QPaintEvent
        """
        option = QtWidgets.QStyleOption()
        option.initFrom(self)
        painter = QtGui.QPainter(self)
        style = self.style()
        style.drawPrimitive(QtWidgets.QStyle.PE_Widget, option, painter, self)

    #############################################
    #   SLOTS
    #################################

    @QtCore.pyqtSlot('QGraphicsScene', 'QGraphicsItem')
    def doAddNode(self, diagram, node):
        """
        Add a node in the tree view.
        :type diagram: QGraphicsScene
        :type node: AbstractItem
        """
        if node.type() in self.items:
            parent = self.parentFor(node)
            if not parent:
                parent = QtGui.QStandardItem(self.parentKey(node))
                parent.setIcon(self.iconFor(node))
                self.model.appendRow(parent)
            child = QtGui.QStandardItem(self.childKey(diagram, node))
            child.setData(node)
            # CHECK FOR DUPLICATE NODES
            children = [parent.child(i) for i in range(parent.rowCount())]
            if not any([child.text() == c.text() for c in children]):
                parent.appendRow(child)
            # APPLY FILTERS AND SORT
            if self.sender() != self.plugin:
                self.proxy.invalidateFilter()
                self.proxy.sort(0, QtCore.Qt.AscendingOrder)

    @QtCore.pyqtSlot(str)
    def doFilterItem(self, key):
        """
        Executed when the search box is filled with data.
        :type key: str
        """
        self.proxy.setFilterFixedString(key)
        self.proxy.sort(QtCore.Qt.AscendingOrder)

    @QtCore.pyqtSlot()
    def doFocusSearch(self):
        """
        Focus the search bar.
        """
        # RAISE THE ENTIRE WIDGET TREE IF IT IS NOT VISIBLE
        if not self.isVisible():
            widget = self
            while widget != self.session:
                widget.show()
                widget.raise_()
                widget = widget.parent()
        self.search.setFocus()
        self.search.selectAll()

    @QtCore.pyqtSlot('QGraphicsScene', 'QGraphicsItem')
    def doRemoveNode(self, diagram, node):
        """
        Remove a node from the tree view.
        :type diagram: QGraphicsScene
        :type node: AbstractItem
        """
        if node.type() in self.items:
            parent = self.parentFor(node)
            if parent:
                child = self.childFor(parent, diagram, node)
                if child:
                    parent.removeRow(child.index().row())
                if not parent.rowCount():
                    self.model.removeRow(parent.index().row())

    @QtCore.pyqtSlot('QModelIndex')
    def onItemActivated(self, index):
        """
        Executed when an item in the treeview is activated (e.g. by pressing Return or Enter key).
        :type index: QModelIndex
        """
        # noinspection PyArgumentList
        if QtWidgets.QApplication.mouseButtons() == QtCore.Qt.NoButton:
            item = self.model.itemFromIndex(self.proxy.mapToSource(index))
            if item and item.data():
                self.sgnItemActivated.emit(item.data())
                # KEEP FOCUS ON THE TREE VIEW UNLESS SHIFT IS PRESSED
                if QtWidgets.QApplication.queryKeyboardModifiers(
                ) & QtCore.Qt.SHIFT:
                    return
                self.ontoview.setFocus()
            elif item:
                # EXPAND/COLLAPSE PARENT ITEM
                if self.ontoview.isExpanded(index):
                    self.ontoview.collapse(index)
                else:
                    self.ontoview.expand(index)

    @QtCore.pyqtSlot('QModelIndex')
    def onItemDoubleClicked(self, index):
        """
        Executed when an item in the treeview is double clicked.
        :type index: QModelIndex
        """
        # noinspection PyArgumentList
        if QtWidgets.QApplication.mouseButtons() & QtCore.Qt.LeftButton:
            item = self.model.itemFromIndex(self.proxy.mapToSource(index))
            if item and item.data():
                self.sgnItemDoubleClicked.emit(item.data())

    @QtCore.pyqtSlot('QModelIndex')
    def onItemPressed(self, index):
        """
        Executed when an item in the treeview is clicked.
        :type index: QModelIndex
        """
        # noinspection PyArgumentList
        if QtWidgets.QApplication.mouseButtons() & QtCore.Qt.LeftButton:
            item = self.model.itemFromIndex(self.proxy.mapToSource(index))
            if item and item.data():
                self.sgnItemClicked.emit(item.data())

    @QtCore.pyqtSlot(bool)
    def onMenuButtonClicked(self, checked=False):
        """
        Executed when a button in the widget menu is clicked.
        """
        # UPDATE THE PALETTE LAYOUT
        data = self.sender().data()
        elems = self.proxy.items if isinstance(data,
                                               Item) else self.proxy.status
        if checked:
            elems.add(data)
        else:
            elems.discard(data)
        self.proxy.invalidateFilter()
        self.proxy.sort(0, QtCore.Qt.AscendingOrder)

    @QtCore.pyqtSlot(Item, str)
    def onMetaUpdated(self, item, name):
        """
        Executed when metadata of the predicate for the given item/name combination is updated
        :type item: Item
        :type name: str
        """
        self.proxy.invalidateFilter()
        self.proxy.sort(0, QtCore.Qt.AscendingOrder)

    @QtCore.pyqtSlot()
    def onReturnPressed(self):
        """
        Executed when the Return or Enter key is pressed in the search field.
        """
        self.focusNextChild()

    #############################################
    #   INTERFACE
    #################################

    def childFor(self, parent, diagram, node):
        """
        Search the item representing this node among parent children.
        :type parent: QtGui.QStandardItem
        :type diagram: Diagram
        :type node: AbstractNode
        """
        key = self.childKey(diagram, node)
        for i in range(parent.rowCount()):
            child = parent.child(i)
            if child.text() == key:
                return child
        return None

    @staticmethod
    def childKey(diagram, node):
        """
        Returns the child key (text) used to place the given node in the treeview.
        :type diagram: Diagram
        :type node: AbstractNode
        :rtype: str
        """
        predicate = node.text().replace('\n', '')
        diagram = rstrip(diagram.name, File.Graphol.extension)
        return '{0} ({1} - {2})'.format(predicate, diagram, node.id)

    def iconFor(self, node):
        """
        Returns the icon for the given node.
        :type node:
        """
        if node.type() is Item.AttributeNode:
            return self.iconAttribute
        if node.type() is Item.ConceptNode:
            return self.iconConcept
        if node.type() is Item.IndividualNode:
            if node.identity() is Identity.Individual:
                return self.iconInstance
            if node.identity() is Identity.Value:
                return self.iconValue
        if node.type() is Item.RoleNode:
            return self.iconRole

    def parentFor(self, node):
        """
        Search the parent element of the given node.
        :type node: AbstractNode
        :rtype: QtGui.QStandardItem
        """
        for i in self.model.findItems(self.parentKey(node),
                                      QtCore.Qt.MatchExactly):
            n = i.child(0).data()
            if node.type() is n.type():
                return i
        return None

    @staticmethod
    def parentKey(node):
        """
        Returns the parent key (text) used to place the given node in the treeview.
        :type node: AbstractNode
        :rtype: str
        """
        return node.text().replace('\n', '')

    def sizeHint(self):
        """
        Returns the recommended size for this widget.
        :rtype: QtCore.QSize
        """
        return QtCore.QSize(216, 266)