コード例 #1
0
	def setEditorData(self, editor: EditorAndDeleterFactory, index: QtCore.QModelIndex):
		editor.lineEdit.setText(index.data())
		
		def deleteRow(self):
			editor.parent().parent().setFocus() #Return focus to the list.
			editor.setParent(None) #If we remove the editor parent, we segfault.
			index.model().removeRow(index.row())
		editor.delete.clicked.connect(deleteRow)
コード例 #2
0
 def setEditorData(  # pylint: disable=C0103
         self, editor: QtWidgets.QWidget,
         index: QtCore.QModelIndex) -> None:
     if index.isValid():
         i = index.data(QtCore.Qt.UserRole)
         if isinstance(editor, widgets.CustomItemWidget):
             editor.data = i.data
     super().setEditorData(editor, index)
コード例 #3
0
 def paint(self, painter: QtGui.QPainter, option,
           index: QtCore.QModelIndex):
     background = index.data(QtCore.Qt.BackgroundRole)
     if isinstance(background, QtGui.QBrush):
         if background.color() != QtCore.Qt.white:
             # WARNING Чтобы drawText работал правильно, нужно чтобы в ui форме в QTable
             # был явно прописан размер шрифта!!!
             painter.fillRect(option.rect, background)
             text_rect = option.rect
             # Не знаю как получить нормальный прямоугольник для отрисовки текста, поэтому только так
             text_rect.setLeft(text_rect.left() + 3)
             painter.drawText(text_rect, option.displayAlignment,
                              index.data())
         else:
             super().paint(painter, option, index)
     else:
         super().paint(painter, option, index)
コード例 #4
0
    def setEditorData(self, editor: QtWidgets.QPushButton,
                      index: QtCore.QModelIndex):

        if index.isValid():
            i = index.data(QtCore.Qt.UserRole)
            if isinstance(editor, options.CustomItemWidget):
                editor.data = i.data
        super().setEditorData(editor, index)
コード例 #5
0
	def scriptStopped(self, index: QtCore.QModelIndex):
		script = index.data(Qt.UserRole)
		
		script['process'] = None
		self.scripts.setData(index, script, Qt.UserRole )
		self.updateRunButton()
		
		log.info(f"stop script {script['path']}")
コード例 #6
0
    def paint(
        self,
        painter: QtGui.QPainter,
        option: QtWidgets.QStyleOptionViewItem,
        idx: QtCore.QModelIndex,
    ) -> None:
        font_family = idx.data(QtCore.Qt.DisplayRole)
        font = QtGui.QFont(option.font)
        font.setPointSize(QtGui.QFontInfo(font).pointSize() * 3 / 2)
        font2 = QtGui.QFont(font)
        font2.setFamily(font_family)

        if option.state & QtWidgets.QStyle.State_Selected:
            painter.save()
            painter.setBrush(option.palette.highlight())
            painter.setPen(QtCore.Qt.NoPen)
            painter.drawRect(option.rect)
            painter.setPen(QtGui.QPen(option.palette.highlightedText(), 0))

        icon = self.bitmap
        if QtGui.QFontDatabase().isSmoothlyScalable(font_family):
            icon = self.truetype
        actual_size = icon.actualSize(option.rect.size())

        icon.paint(painter, option.rect,
                   QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
        if option.direction == QtCore.Qt.RightToLeft:
            option.rect.setRight(option.rect.right() - actual_size.width() - 4)
        else:
            option.rect.setLeft(option.rect.left() + actual_size.width() + 4)

        half1 = QtCore.QRect(option.rect)
        half2 = QtCore.QRect(option.rect)
        half1.setRight(half1.right() / 2)
        half2.setLeft(half1.right())

        painter.drawText(
            half1,
            QtCore.Qt.AlignVCenter
            | QtCore.Qt.AlignLeading
            | QtCore.Qt.TextSingleLine,
            font_family,
        )

        old = painter.font()
        painter.setFont(font2)
        painter.drawText(
            half2,
            QtCore.Qt.AlignVCenter
            | QtCore.Qt.AlignLeading
            | QtCore.Qt.TextSingleLine,
            self.sample_text,
        )
        painter.setFont(old)

        if option.state & QtWidgets.QStyle.State_Selected:
            painter.restore()
コード例 #7
0
 def setModelData(self, editor: QWidget, model: QAbstractItemModel,
                  index: QModelIndex) -> None:
     # if index.data():
     #     model.setData(index, str(self.mTxt_address.text()))
     print(index.data())
     if isinstance(editor, FileAddressEditor):
         model.setData(index, editor.text())
     else:
         super(addressTableDelegate,
               self).setModelData(editor, model, index)
コード例 #8
0
ファイル: actor_view.py プロジェクト: zeldamods/event-editor
    def removeActor(self, idx: qc.QModelIndex) -> None:
        actor = idx.data(qc.Qt.UserRole)
        if util.is_actor_in_use(self.flow_data.flow.flowchart.events, actor):
            q.QMessageBox.critical(
                self, 'Cannot remove actor',
                f'{actor.identifier} cannot be removed because it is used by events. Please remove any references to this actor and try again.'
            )
            return

        self.flow_data.actor_model.remove(actor)
コード例 #9
0
    def cFlagListDoubleClick(self, model_index: QModelIndex) -> None:
        """
        @param model_index: model index that was double clicked
        @type model_index: QModelIndex
        @return: None
        @rtype: None
        """

        self.appendCurrentCFlags(model_index.data())
        self.View.cflagsList.setFocus(True)
コード例 #10
0
 def getLinkedItem(self, index: QModelIndex) -> QModelIndex:
     if not index.data(IS_LINK_ROLE):
         return QModelIndex()
     try:
         reference = self.data(index, OBJECT_ROLE)
         objStore = self.data(index, PACKAGE_ROLE).objStore
         obj = reference.resolve(objStore)
         linkedPackItem, = self.data(index, PACK_ITEM_ROLE).model().match(QModelIndex(), OBJECT_ROLE, obj, hits=1)
         return linkedPackItem
     except AttributeError:
         return QModelIndex()
コード例 #11
0
    def cFlagCurrentDoubleClicked(self, modelIndex: QModelIndex) -> None:
        """
        @param modelIndex:
        @type modelIndex:
        @return:
        @rtype:
        """

        self.removeCurrentCFlag(modelIndex.data())
        self.View.cflagsCurrentList.setFocus(True)

        return None
コード例 #12
0
ファイル: SectionComboBoxDelegate.py プロジェクト: jopohl/urh
 def paint(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex):
     item_type = index.data(Qt.AccessibleDescriptionRole)
     if item_type == "parent":
         parent_option = option
         parent_option.state |= QStyle.State_Enabled
         super().paint(painter, parent_option, index)
     elif item_type == "child":
         child_option = option
         indent = option.fontMetrics.width(4 * " ")
         child_option.rect.adjust(indent, 0, 0, 0)
         child_option.textElideMode = Qt.ElideNone
         super().paint(painter, child_option, index)
     else:
         super().paint(painter, option, index)
コード例 #13
0
    def selection_changed(self, curr_index: QModelIndex, prev_index: QModelIndex):
        """
        Updates the information about currently selected account.
        """
        if not curr_index.isValid():
            return None

        # Make sure selection is visible in the view
        self.selection.setCurrentIndex(
            curr_index, QItemSelectionModel.SelectCurrent)

        acc = curr_index.data(role=Qt.UserRole)

        # Set the type of account
        self.typeBox.setCurrentText(acc.type)

        # Set the checkboxes
        self.closedBox.setChecked(acc.closed)
        self.exBudgetBox.setChecked(acc.exbudget)
コード例 #14
0
ファイル: main.py プロジェクト: wordtinker/SimpleBudget
    def account_clicked(self, index: QModelIndex):
        """
        Opens the list of transactions for given index in the account
        tree model.
        """
        account = index.data(role=Qt.UserRole)
        if not isinstance(account, Account):
            return

        transaction_manager = TransactionsRoll(self.orm, account)

        self.menuBar.setEnabled(False)

        transaction_manager.exec()

        self.menuBar.setEnabled(True)

        # Update if there was changes
        self.show_accounts()
        self.show_budget_report()
コード例 #15
0
ファイル: textEditView.py プロジェクト: olivierkes/manuskript
class textEditView(QTextEdit):
    def __init__(self, parent=None, index=None, html=None, spellcheck=None,
                 highlighting=False, dict="", autoResize=False):
        QTextEdit.__init__(self, parent)
        self._column = Outline.text
        self._index = None
        self._indexes = None
        self._model = None
        self._placeholderText = self.placeholderText()
        self._updating = False
        self._item = None
        self._highlighting = highlighting
        self._textFormat = "text"
        self.setAcceptRichText(False)
        # When setting up a theme, this becomes true.
        self._fromTheme = False
        self._themeData = None
        self._highlighterClass = BasicHighlighter

        if spellcheck is None:
            spellcheck = settings.spellcheck

        self.spellcheck = spellcheck
        self.currentDict = dict if dict else settings.dict
        self._defaultFontSize = qApp.font().pointSize()
        self.highlighter = None
        self.setAutoResize(autoResize)
        self._defaultBlockFormat = QTextBlockFormat()
        self._defaultCharFormat = QTextCharFormat()
        self.highlightWord = ""
        self.highligtCS = False
        self._dict = None
        # self.document().contentsChanged.connect(self.submit, F.AUC)

        # Submit text changed only after 500ms without modifications
        self.updateTimer = QTimer()
        self.updateTimer.setInterval(500)
        self.updateTimer.setSingleShot(True)
        self.updateTimer.timeout.connect(self.submit)
        # self.updateTimer.timeout.connect(lambda: print("Timeout"))

        self.updateTimer.stop()
        self.document().contentsChanged.connect(self.updateTimer.start, F.AUC)
        # self.document().contentsChanged.connect(lambda: print("Document changed"))

        # self.document().contentsChanged.connect(lambda: print(self.objectName(), "Contents changed"))

        self.setEnabled(False)

        if index:
            self.setCurrentModelIndex(index)

        elif html:
            self.document().setHtml(html)
            self.setReadOnly(True)

        # Spellchecking
        if enchant and self.spellcheck:
            try:
                self._dict = enchant.Dict(self.currentDict if self.currentDict
                                          else self.getDefaultLocale())
            except enchant.errors.DictNotFoundError:
                self.spellcheck = False

        else:
            self.spellcheck = False

        if self._highlighting and not self.highlighter:
            self.highlighter = self._highlighterClass(self)
            self.highlighter.setDefaultBlockFormat(self._defaultBlockFormat)

    def getDefaultLocale(self):
        default_locale = enchant.get_default_language()
        if default_locale is None:
            default_locale = QLocale.system().name()
        if default_locale is None:
            default_locale = enchant.list_dicts()[0][0]

        return default_locale

    def setModel(self, model):
        self._model = model
        try:
            self._model.dataChanged.connect(self.update, F.AUC)
        except TypeError:
            pass

    def setColumn(self, col):
        self._column = col

    def setHighlighting(self, val):
        self._highlighting = val

    def setDefaultBlockFormat(self, bf):
        self._defaultBlockFormat = bf
        if self.highlighter:
            self.highlighter.setDefaultBlockFormat(bf)

    def setCurrentModelIndex(self, index):
        self._indexes = None
        if index.isValid():
            self.setEnabled(True)
            if index.column() != self._column:
                index = index.sibling(index.row(), self._column)
            self._index = QPersistentModelIndex(index)

            self.setPlaceholderText(self._placeholderText)

            if not self._model:
                self.setModel(index.model())

            self.setupEditorForIndex(self._index)
            self.loadFontSettings()
            self.updateText()

        else:
            self._index = QModelIndex()

            self.setPlainText("")
            self.setEnabled(False)

    def currentIndex(self):
        """
        Getter function used to normalized views access with QAbstractItemViews.
        """
        if self._index:
            return self._index
        else:
            return QModelIndex()

    def getSelection(self):
        """
        Getter function used to normalized views access with QAbstractItemViews.
        """
        return [self.currentIndex()]

    def setCurrentModelIndexes(self, indexes):
        self._index = None
        self._indexes = []

        for i in indexes:
            if i.isValid():
                self.setEnabled(True)
                if i.column() != self._column:
                    i = i.sibling(i.row(), self._column)
                self._indexes.append(QModelIndex(i))

                if not self._model:
                    self.setModel(i.model())

        self.updateText()

    def setupEditorForIndex(self, index):
        # Setting highlighter
        if self._highlighting:
            self.highlighter = self._highlighterClass(self)
            self.highlighter.setDefaultBlockFormat(self._defaultBlockFormat)
            self.highlighter.updateColorScheme()

    def loadFontSettings(self):
        if self._fromTheme or \
                not self._index or \
                    type(self._index.model()) != outlineModel or \
                    self._column != Outline.text:
            return

        opt = settings.textEditor
        f = QFont()
        f.fromString(opt["font"])
        background = (opt["background"] if not opt["backgroundTransparent"]
                      else "transparent")
        foreground = opt["fontColor"] # if not opt["backgroundTransparent"]
        #                               else S.text
        # self.setFont(f)
        self.setStyleSheet("""QTextEdit{{
            background: {bg};
            color: {foreground};
            font-family: {ff};
            font-size: {fs};
            margin: {mTB}px {mLR}px;
            {maxWidth}
            }}
            """.format(
                bg=background,
                foreground=foreground,
                ff=f.family(),
                fs="{}pt".format(str(f.pointSize())),
                mTB = opt["marginsTB"],
                mLR = opt["marginsLR"],
                maxWidth = "max-width: {}px;".format(opt["maxWidth"]) if opt["maxWidth"] else "",
                )
            )
        self._defaultFontSize = f.pointSize()

        # We set the parent background to the editor's background in case
        # there are margins. We check that the parent class is a QWidget because
        # if textEditView is used in fullScreenEditor, then we don't want to
        # set the background.
        if self.parent().__class__ == QWidget:
            self.parent().setStyleSheet("""
                QWidget#{name}{{
                    background: {bg};
                }}""".format(
                    # We style by name, otherwise all inheriting widgets get the same
                    # colored background, for example context menu.
                    name=self.parent().objectName(),
                    bg=background,
                ))

        cf = QTextCharFormat()
        # cf.setFont(f)
        # cf.setForeground(QColor(opt["fontColor"]))

        self.setCursorWidth(opt["cursorWidth"])

        bf = QTextBlockFormat()
        bf.setLineHeight(opt["lineSpacing"], bf.ProportionalHeight)
        bf.setTextIndent(opt["tabWidth"] * 1 if opt["indent"] else 0)
        bf.setTopMargin(opt["spacingAbove"])
        bf.setBottomMargin(opt["spacingBelow"])
        bf.setAlignment(Qt.AlignLeft if opt["textAlignment"] == 0 else
                        Qt.AlignCenter if opt["textAlignment"] == 1 else
                        Qt.AlignRight if opt["textAlignment"] == 2 else
                        Qt.AlignJustify)

        self._defaultCharFormat = cf
        self._defaultBlockFormat = bf

        if self.highlighter:
            self.highlighter.updateColorScheme()
            self.highlighter.setMisspelledColor(QColor(opt["misspelled"]))
            self.highlighter.setDefaultCharFormat(self._defaultCharFormat)
            self.highlighter.setDefaultBlockFormat(self._defaultBlockFormat)

    def update(self, topLeft, bottomRight):
        if self._updating:
            return

        if self._index and self._index.isValid():

            if topLeft.parent() != self._index.parent():
                return

                # print("Model changed: ({}:{}), ({}:{}/{}), ({}:{}) for {} of {}".format(
                # topLeft.row(), topLeft.column(),
                # self._index.row(), self._index.row(), self._column,
                # bottomRight.row(), bottomRight.column(),
                # self.objectName(), self.parent().objectName()))

            if topLeft.row() <= self._index.row() <= bottomRight.row():
                if topLeft.column() <= self._column <= bottomRight.column():
                    self.updateText()

        elif self._indexes:
            update = False
            for i in self._indexes:
                if topLeft.row() <= i.row() <= bottomRight.row():
                    update = True
            if update:
                self.updateText()

    def disconnectDocument(self):
        try:
            self.document().contentsChanged.disconnect(self.updateTimer.start)
        except:
            pass

    def reconnectDocument(self):
        self.document().contentsChanged.connect(self.updateTimer.start, F.AUC)

    def updateText(self):
        if self._updating:
            return
        # print("Updating", self.objectName())
        self._updating = True
        if self._index:
            self.disconnectDocument()
            if self.toPlainText() != F.toString(self._index.data()):
                # print("    Updating plaintext")
                self.document().setPlainText(F.toString(self._index.data()))
            self.reconnectDocument()

        elif self._indexes:
            self.disconnectDocument()
            t = []
            same = True
            for i in self._indexes:
                item = i.internalPointer()
                t.append(F.toString(item.data(self._column)))

            for t2 in t[1:]:
                if t2 != t[0]:
                    same = False
                    break

            if same:
                self.document().setPlainText(t[0])
            else:
                self.document().setPlainText("")

                if not self._placeholderText:
                    self._placeholderText = self.placeholderText()

                self.setPlaceholderText(self.tr("Various"))
            self.reconnectDocument()
        self._updating = False

    def submit(self):
        self.updateTimer.stop()
        if self._updating:
            return
        # print("Submitting", self.objectName())
        if self._index and self._index.isValid():
            # item = self._index.internalPointer()
            if self.toPlainText() != self._index.data():
                # print("    Submitting plain text")
                self._updating = True
                self._model.setData(QModelIndex(self._index),
                                    self.toPlainText())
                self._updating = False

        elif self._indexes:
            self._updating = True
            for i in self._indexes:
                item = i.internalPointer()
                if self.toPlainText() != F.toString(item.data(self._column)):
                    print("Submitting many indexes")
                    self._model.setData(i, self.toPlainText())
            self._updating = False

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_V and event.modifiers() & Qt.ControlModifier:
            text = QApplication.clipboard().text()
            self.insertPlainText(text)
        else:
            QTextEdit.keyPressEvent(self, event)

        if event.key() == Qt.Key_Space:
            self.submit()

    # -----------------------------------------------------------------------------------------------------
    # Resize stuff

    def resizeEvent(self, e):
        QTextEdit.resizeEvent(self, e)
        if self._autoResize:
            self.sizeChange()

    def sizeChange(self):
        opt = settings.textEditor
        docHeight = self.document().size().height() + 2 * opt["marginsTB"]
        if self.heightMin <= docHeight <= self.heightMax:
            self.setMinimumHeight(docHeight)

    def setAutoResize(self, val):
        self._autoResize = val
        if self._autoResize:
            self.document().contentsChanged.connect(self.sizeChange)
            self.heightMin = 0
            self.heightMax = 65000
            self.sizeChange()

        ###############################################################################
        # SPELLCHECKING
        ###############################################################################
        # Based on http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/

    def setDict(self, d):
        self.currentDict = d
        if d and enchant.dict_exists(d):
            self._dict = enchant.Dict(d)
        if self.highlighter:
            self.highlighter.rehighlight()

    def toggleSpellcheck(self, v):
        self.spellcheck = v
        if enchant and self.spellcheck and not self._dict:
            if self.currentDict and enchant.dict_exists(self.currentDict):
                self._dict = enchant.Dict(self.currentDict)
            elif enchant.get_default_language() and enchant.dict_exists(enchant.get_default_language()):
                self._dict = enchant.Dict(enchant.get_default_language())
            else:
                self.spellcheck = False

        if self.highlighter:
            self.highlighter.rehighlight()
        else:
            self.spellcheck = False

    def mousePressEvent(self, event):
        if event.button() == Qt.RightButton:
            # Rewrite the mouse event to a left button event so the cursor is
            # moved to the location of the pointer.
            event = QMouseEvent(QEvent.MouseButtonPress, event.pos(),
                                Qt.LeftButton, Qt.LeftButton, Qt.NoModifier)
        QTextEdit.mousePressEvent(self, event)

    def wheelEvent(self, event):
        """
        We catch wheelEvent if key modifier is CTRL to change font size.
        Note: this should be in a class specific for main textEditView (#TODO).
        """
        if event.modifiers() & Qt.ControlModifier:
            # Get the wheel angle.
            d = event.angleDelta().y() / 120

            # Update settings
            f = QFont()
            f.fromString(settings.textEditor["font"])
            f.setPointSizeF(f.pointSizeF() + d)
            settings.textEditor["font"] = f.toString()

            # Update font to all textEditView. Drastically.
            for w in F.mainWindow().findChildren(textEditView, QRegExp(".*")):
                w.loadFontSettings()

            # We tell the world that we accepted this event
            event.accept()
            return

        QTextEdit.wheelEvent(self, event)

    class SpellAction(QAction):
        """A special QAction that returns the text in a signal. Used for spellcheck."""

        correct = pyqtSignal(str)

        def __init__(self, *args):
            QAction.__init__(self, *args)

            self.triggered.connect(lambda x: self.correct.emit(
                    str(self.text())))

    def contextMenuEvent(self, event):
        # Based on http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
        popup_menu = self.createStandardContextMenu()
        popup_menu.exec_(event.globalPos())

    def createStandardContextMenu(self):
        popup_menu = QTextEdit.createStandardContextMenu(self)

        if not self.spellcheck:
            return popup_menu

        # Select the word under the cursor.
        # But only if there is no selection (otherwise it's impossible to select more text to copy/cut)
        cursor = self.textCursor()
        if not cursor.hasSelection():
            cursor.select(QTextCursor.WordUnderCursor)
            self.setTextCursor(cursor)

        # Check if the selected word is misspelled and offer spelling
        # suggestions if it is.
        if self._dict and cursor.hasSelection():
            text = str(cursor.selectedText())
            valid = self._dict.check(text)
            selectedWord = cursor.selectedText()
            if not valid:
                spell_menu = QMenu(self.tr('Spelling Suggestions'), self)
                spell_menu.setIcon(F.themeIcon("spelling"))
                for word in self._dict.suggest(text):
                    action = self.SpellAction(word, spell_menu)
                    action.correct.connect(self.correctWord)
                    spell_menu.addAction(action)
                # Only add the spelling suggests to the menu if there are
                # suggestions.
                if len(spell_menu.actions()) != 0:
                    popup_menu.insertSeparator(popup_menu.actions()[0])
                    # Adds: add to dictionary
                    addAction = QAction(self.tr("&Add to dictionary"), popup_menu)
                    addAction.setIcon(QIcon.fromTheme("list-add"))
                    addAction.triggered.connect(self.addWordToDict)
                    addAction.setData(selectedWord)
                    popup_menu.insertAction(popup_menu.actions()[0], addAction)
                    # Adds: suggestions
                    popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
                    # popup_menu.insertSeparator(popup_menu.actions()[0])

            # If word was added to custom dict, give the possibility to remove it
            elif valid and self._dict.is_added(selectedWord):
                popup_menu.insertSeparator(popup_menu.actions()[0])
                # Adds: remove from dictionary
                rmAction = QAction(self.tr("&Remove from custom dictionary"), popup_menu)
                rmAction.setIcon(QIcon.fromTheme("list-remove"))
                rmAction.triggered.connect(self.rmWordFromDict)
                rmAction.setData(selectedWord)
                popup_menu.insertAction(popup_menu.actions()[0], rmAction)

        return popup_menu

    def correctWord(self, word):
        """
        Replaces the selected text with word.
        """
        cursor = self.textCursor()
        cursor.beginEditBlock()

        cursor.removeSelectedText()
        cursor.insertText(word)

        cursor.endEditBlock()

    def addWordToDict(self):
        word = self.sender().data()
        self._dict.add(word)
        self.highlighter.rehighlight()

    def rmWordFromDict(self):
        word = self.sender().data()
        self._dict.remove(word)
        self.highlighter.rehighlight()

    ###############################################################################
    # FORMATTING
    ###############################################################################

    def focusOutEvent(self, event):
        """Submit changes just before focusing out."""
        QTextEdit.focusOutEvent(self, event)
        self.submit()

    ###############################################################################
    # KEYBOARD SHORTCUTS
    ###############################################################################

    def callMainTreeView(self, functionName):
        """
        The tree view in main window must have same index as the text
        edit that has focus. So we can pass it the call for documents
        edits like: duplicate, move up, etc.
        """
        if self._index and self._column == Outline.text:
            function = getattr(F.mainWindow().treeRedacOutline, functionName)
            function()

    def rename(self): self.callMainTreeView("rename")
    def duplicate(self): self.callMainTreeView("duplicate")
    def moveUp(self): self.callMainTreeView("moveUp")
    def moveDown(self): self.callMainTreeView("moveDown")
コード例 #16
0
class TreeModel(QAbstractItemModel):
    # Funktion hasChildren?
    
    # signals
    statusChanged = pyqtSignal(QModelIndex)
    speciesChanged = pyqtSignal(QModelIndex, int, int)
    calculated = pyqtSignal()
    
    itemsInserted = pyqtSignal(bool)
    itemsAboutToBeCalculated = pyqtSignal(bool)
    allItemsRemoved = pyqtSignal(bool)

    # class constants
    ItemRole = Qt.UserRole + 1
    StatusRole = Qt.UserRole + 2
    ColorRole = Qt.UserRole + 3
    TypeRole = Qt.UserRole + 4
    NameRole = Qt.UserRole + 5
    ResultRole = Qt.UserRole + 6
    PlantRole = Qt.UserRole + 7
    ProtectionRole = Qt.UserRole + 8
    SpeciesRole = Qt.UserRole + 9
    TypeRole = Qt.UserRole + 10
    IdentificationRole = Qt.UserRole + 11
    LengthRole = Qt.UserRole + 12
    CountRole = Qt.UserRole + 13
    _roles = {ItemRole : "item",
              StatusRole : "status",
              ColorRole : "color",
              TypeRole : "type",
              NameRole : "name",
              ResultRole : "result",
              PlantRole : "plant",
              ProtectionRole : "protection",
              IdentificationRole : "identification",
              LengthRole : "length",
              CountRole : "count"}

    TYPE, IDENTIFICATION, SPECIES, NAME = range(4)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.root = VariantItem("root item")
        
        # species registry
        self.species = {}
        self.variants = 0

        # initialize class attributes
        self._TABLE_HEADER_LABELS = (QtCore.QT_TRANSLATE_NOOP("TreeModel", "Protection type"),
                                     QtCore.QT_TRANSLATE_NOOP("TreeModel", "Name"),
                                     QtCore.QT_TRANSLATE_NOOP("TreeModel", "Tree species"),
                                     QtCore.QT_TRANSLATE_NOOP("TreeModel", "Description of protection"))
        
        # project settings
        self.project = Project()
        
        self.rowsInserted.connect(self.updateSpecies)
        self.speciesChanged.connect(self.moveItem)
        self.statusChanged.connect(self.updateStatus)
        
        self.new = True
        self.changed = False
        self.filename = ""
        self.file = False
        self.read = False
        
        self.last = QModelIndex()
        self.current = -1
        
        self.count = 0          # temoporary plant count for calculation help
        self.length = 0         # temoporary fence length for calculation help
    
    def columnCount(self, parent):
        return len(self._TABLE_HEADER_LABELS)
        
    def roleNames(self):
        return self._roles
    
    def projectData(self, key):
        return getattr(self.project, key)
        
    def setProjectData(self, key, value):
        setattr(self.project, key, value)
        self.changed = True
    
    def itemData(self, index):
        if not index.isValid():
            return None
        
        item = self.getItem(index)
        
        # create the QMap as python dict
        data = {
            self.NameRole : item.name,
            self.ColorRole : item.color,
            self.StatusRole : item.status,
            self.PlantRole : item.plant,
            self.ProtectionRole : item.protection
        }
        
        return data
    
    def setItemData(self, index, roles):
        if not index.isValid():
            return False
        
        item = self.getItem(index)
        oldSpecies = item.plant.species
        newSpecies = roles[self.PlantRole].species
        
        # if the species has changed, the item have to move
        # a fence item doesn't have already known species
        if not newSpecies == oldSpecies:
            if item.type == Fence.TYPE and newSpecies in self.species:
                return False
            
            self.speciesChanged.emit(index, oldSpecies, newSpecies)
        
        # update the item
        for role in roles:
            if not role in self._roles:
                return False
            
            attribute = self._roles[role]
            setattr(item, attribute, roles[role])
        
        # update model's change status
        self.dataChanged.emit(index, index)
        self.changed = True
        
        return True
    
    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return None
        
        item = index.internalPointer()
        column = index.column()
        
        if role == Qt.DisplayRole:
            if column == TreeModel.TYPE:
                return QApplication.translate("VariantItem", item.protection.TYPE_DESCRIPTION)
            elif column == TreeModel.NAME:
                return item.name
            elif column == TreeModel.SPECIES:
                return library.TREESPECIES_ABBREVIATION[item.plant.species]
            elif column == TreeModel.IDENTIFICATION:
                return "{protection}{identification}".format(
                        protection=QApplication.translate("VariantItem", item.protection.TYPE_SHORT),
                        identification=item.identification + 1)
        elif role == Qt.CheckStateRole:
            if column == TreeModel.TYPE:
                if item.status:
                    if item.type == Fence.TYPE:
                        checked = True
                        for child in item.children:
                            if not child.status:
                                checked = False
                        if checked:
                            return Qt.Checked
                        else:
                            return Qt.PartiallyChecked
                    else:
                        return Qt.Checked
                else:
                    return Qt.Unchecked
        elif role == self.IdentificationRole:
            return "{protection}{identification}".format(
                    protection=QApplication.translate("VariantItem", item.protection.TYPE_SHORT),
                    identification=item.identification + 1)
        elif role == self.LengthRole:
            return self.project.length
        elif role == self.CountRole:
            return self.project.count
        elif role == self.TypeRole:
            return item.type
        elif role == self.NameRole:
            return item.name
        elif role == self.SpeciesRole:
            return item.plant.species
        elif role == self.ColorRole:
            return QColor(item.color)
        elif role == self.StatusRole:
            return item.status
        elif role == self.ResultRole:
            return item.result
        else:
            return None
    
    def setData(self, index, value, role=Qt.EditRole):
        item = index.internalPointer()

        if role == Qt.CheckStateRole:
            if value in (Qt.Checked, Qt.PartiallyChecked):
                item.status = True
            else:
                item.status = False
            
            self.dataChanged.emit(index, index)
            #self.statusChanged.emit(index)
                
            if item.type == Fence.TYPE:
                for child in item.children:
                    child.status = item.status
                    childIndex = self.createIndex(child.childNumber(), self.TYPE, child)
                    self.dataChanged.emit(childIndex, childIndex)
            if item.type > Fence.TYPE:
                parent = item.parent
                status = False
                for child in parent.children:
                    if child.status:
                        status = True
            
                parent.status = status
                
                self.dataChanged.emit(index.parent(), index.parent())
            
            return True
            
            return True
        elif role == self.ResultRole:
            item.result = value
            self.dataChanged.emit(index, index)

            return True
        elif role == self.ColorRole:
            item.color = value
            self.dataChanged.emit(index, index)
            
            return True
        else:
            return False

    def flags(self, index):
        if not index.isValid():
            return Qt.NoItemFlags
        
        flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
        column = index.column()
        if column == TreeModel.TYPE:
            if index.data() == Fence.TYPE_DESCRIPTION:
                return flags | Qt.ItemIsUserCheckable | 64
            else:
                return flags | Qt.ItemIsUserCheckable
        else:
            return flags

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return QApplication.translate("TreeModel", self._TABLE_HEADER_LABELS[section])
        
        return None
    
    def index(self, row, column, parent=QModelIndex()):     # kann Fehler verursachen!
        if not self.hasIndex(row, column, parent):
            return QModelIndex()
            
        if not parent.isValid():
            parentItem = self.root
        else:
            parentItem = parent.internalPointer()
            
        childItem = parentItem.child(row)
        if childItem:
            return self.createIndex(row, column, childItem)
        else:
            return QModelIndex()
    
    def parent(self, index):
        if not index.isValid():
            return QModelIndex()
        
        child = self.getItem(index)
        parent = child.parent
        
        if parent == self.root:
            return QModelIndex()
        
        return self.createIndex(parent.childNumber(), 0, parent)
    
    def rowCount(self, parent=QModelIndex()):
        #if parent.column() > 0:
        #    return 0
        
        #if not parent.isValid():
        #    parent_item = self.root
        #else:
        #    parent_item = parent.internalPointer()
        
        #return parent_item.childCount()
        
        item = self.getItem(parent)
        
        return item.childCount()
    
    def evaluate(self):
        evaluation = False

        # if there is a fence item with at least 
        # one children, enable the calculation button
        items = self.match(self.index(0, 0),
                self.TypeRole, Tube.TYPE, -1, Qt.MatchRecursive)

        for item in items:
            if item.parent().isValid():
                # the calculation button should be enabled
                evaluation = True
                break
        
        self.itemsAboutToBeCalculated.emit(evaluation)
        
        # automatic protection type
        # self.last contains an index value
        if not self.last.parent().isValid() and self.current == -1:
            if self.last.data(self.TypeRole) == Fence.TYPE:
                self.current = Tube.TYPE
            elif self.last.data(self.TypeRole) == Tube.TYPE:
                self.current = Fence.TYPE
            else:
                self.current = -1
        else:
            self.current = -1
        
        return evaluation
    
    def getItem(self, index):
        if index.isValid():
            item = index.internalPointer()
            if item:
                return item
        
        return self.root
    
    def insertItem(self, item):
        return self.insertItems([item])
    
    def insertItems(self, items):
        if not items:
            return False

        # extract items with type = 0 as top level items
        toplevel = []
        species = []
        for item in items[:]:
            # update the identification
            item.identification = self.variants
            self.variants = self.variants + 1
        
            if item.type == 0:
                if not item.plant.species in self.species:
                    toplevel.append(item)
                    species.append(item.plant.species)
                    items.remove(item)
                else:
                    return False
            elif item.type > 0:
                if item.plant.species in self.species:
                    parent = self.root.child(self.species[item.plant.species])
                    index = self.createIndex(parent.childCount(), 0, parent)
                    self.insertRow(parent.childCount(), item, index)
                    items.remove(item)
            else:
                return False
        
        # extract current top level items with type > 0 from root
        if toplevel:
            current = []
            for child in self.root.children[:]:
                if child.plant.species in species:
                    current.append(child)
                    self.removeRow(child.childNumber())

            items.extend(current)
            for item in toplevel:
                for child in items[:]:
                    if child.plant.species == item.plant.species:
                        item.insertChildren(item.childCount(), [child])
                        items.remove(child)
            toplevel.extend(items)
        else:
            toplevel = items
        
        # insert the new rows and emit the signal to enable the view
        # also do an evaluation
        if toplevel:
            self.insertRows(self.root.childCount(), toplevel)
        
        # always evaluate
        self.itemsInserted.emit(True)
        self.evaluate()

        return True

    def insertRow(self, row, item, parent=QModelIndex()):
        return self.insertRows(row, [item], parent)
    
    def insertRows(self, row, items, parent=QModelIndex()):
        item = self.getItem(parent)
        
        self.beginInsertRows(parent, row, row + len(items) - 1)
        item.insertChildren(row, items)
        self.endInsertRows()
        
        # get the index of the last item
        if not self.read:
            self.last = self.index(row + len(items) -1, 0, parent)
        
        # update model's change status
        self.new = False
        self.changed = True
        
        return True
    
    def updateStatus(self, index):
        # möglicherweise sollte diese Funktion in die Delegate
        #if not index.parent().isValid():    # item.type = Fence.TYPE
        item = self.getItem(index)
        
        if item.type == Fence.TYPE:
            for child in item.children:
                child.status = item.status
                childIndex = self.createIndex(child.childNumber(), self.TYPE, child)
                self.dataChanged.emit(childIndex, childIndex)
        if item.type > Fence.TYPE:
            parent = item.parent
            status = False
            for child in parent.children:
                if child.status:
                    status = True
            
            parent.status = status
            self.dataChanged.emit(index.parent(), index.parent())
    
    def updateSpecies(self, parent, first, last):
        if not parent.isValid():
            item = self.getItem(parent)
            for row in range(first, last + 1):
                child = item.child(row)
                if child.type == Fence.TYPE:
                    self.species[child.plant.species] = row
    
    def removeItem(self, position, parent=QModelIndex()):
        return self.removeItems(position, 1, parent)
            
    def removeItems(self, position, rows, parent=QModelIndex()):
        item = self.getItem(parent)
        if position < 0 or position + rows - 1 > item.childCount():
            return False
        
        # first it's necessary to remove the species entry
        for row in range(position, position + rows):
            child = item.child(row)
            if child.type == Fence.TYPE:
                del self.species[child.plant.species]
        
        # now the child items can be removed
        # and do an evaluation
        self.removeRows(position, rows, parent)
        self.evaluate()
        
        # if the model is empty, emit the signal
        # to disable the view
        if not self.rowCount():
            self.allItemsRemoved.emit(True)
        
        return True
    
    def removeRow(self, row, parent=QModelIndex()):
        return self.removeRows(row, 1, parent)
    
    def removeRows(self, row, count, parent=QModelIndex()):
        item = self.getItem(parent)
        
        self.beginRemoveRows(parent, row, row + count -1)
        item.removeChildren(row, count)
        self.endRemoveRows()
        
        # get the index of the last item
        self.last = QModelIndex()
        
        # update model's change status
        self.changed = True
        
        return True
    
    def moveItem(self, index, oldSpecies, newSpecies):
        # item.type durch index.parent is valid ersetzen?!
        item = self.getItem(index)
        parent = item.parent
        
        if item.type == Fence.TYPE:
            # first update the species registry
            del self.species[oldSpecies]
            self.species[newSpecies] = index.row()
            
            # simply clear item's internal list and
            # move child item's to tree root
            if item.hasChildren():
                count = item.childCount()
                child = parent.childCount()
                self.moveRows(index, 0, count, index.parent(), child)
            
            # if there are any child items with the same species
            # within tree root, add them to the current item
            for child in parent.children[:]:
                if child.type > Fence.TYPE and child.plant.species == newSpecies:
                    count = item.childCount()
                    row = child.childNumber()
                    self.moveRow(index.parent(), row, index, count)
        
        elif item.type > Fence.TYPE:
            if newSpecies in self.species:
                newParent = self.root.child(self.species[newSpecies])
                newIndex = self.createIndex(newParent.childNumber(), 0, newParent)
                child = newParent.childCount()
                self.moveRow(index.parent(), index.row(), newIndex, child)
            elif index.parent().isValid():
                # only if the parent is valid, the item
                # can be moved to the tree root
                child = self.root.childCount()
                self.moveRow(index.parent(), index.row(), QModelIndex(), child)
        
        # after moving items do an evaluation
        #self.evaluate()
    
    def moveRow(self, source, row, destination, child):
        return self.moveRows(source, row, 1, destination, child)
    
    def moveRows(self, source, row, count, destination, child):
        sourceParent = self.getItem(source)
        destinationParent = self.getItem(destination)
        
        self.beginMoveRows(source, row, row + count - 1, destination, child)
        destinationParent.insertChildren(child, sourceParent.children[row:row+count])
        sourceParent.removeChildren(row, count)
        self.endMoveRows()
        
        # update model's change status
        self.changed = True
        
        return True
    
    def saveFile(self, xmlfile=""):
        if xmlfile:
            self.filename = xmlfile
            self.file = True
        elif self.file:
            xmlfile = self.filename
        else:
            return False
    
        writer = XMLFileWriter()
        success = writer.writeXMLFile(self.project.__dict__, self.root, xmlfile)
        
        # update model's change status
        if success:
            self.changed = False
        
        return success
    
    def readFile(self, xmlfile):
        if xmlfile:
            self.filename = xmlfile
            self.file = True
        else:
            return False
        
        # wichtig, um keine Vorauswahl zu haben
        self.read = True
        reader = XMLFileReader()
        success = reader.readXMLFile(xmlfile)
        
        if success:
            if not self.project.update(reader.getProject()):
                success = False
            if not self.insertItems(reader.getItems()):
                success = False
            
            # TODO
            self.dataChanged.emit(QModelIndex(), QModelIndex())

            # update model's change status and clear model if necessary
            self.new = False
            self.changed = success
            self.read = False
            
            if not success:
                self.clear()
        
        return success
    
    def clear(self):
        # first call beginResetModel()
        self.beginResetModel()
        
        # now clear all model data
        self.project = Project()
        self.root = VariantItem("root item")
        self.species = {}
        self.variants = 0
        self.new = True
        self.changed = False
        self.filename = ""
        self.file = False
        self.read = False
        
        self.last = QModelIndex()
        self.current = -1
        
        self.count = 0
        self.length = 0
        
        # it's necessary to call endResetModel()
        self.endResetModel()
    
    def calculate(self):
        shelters = []
        for child in self.root.children:
            if child.type == Fence.TYPE and child.hasChildren():
                # fence result is set to fence length
                #child.result = child.protection.length
                
                for shelter in child.children:
                    # the following operations are based on the example calculation
                    # of Dr. Anton Hammer <*****@*****.**>
                    slope = shelter.sumCosts() * (1 - shelter.plant.mortality) - child.sumCosts()
                    result = child.protection.installation / slope
                    
                    # update result in model's item
                    index = self.createIndex(shelter.childNumber(), 0, shelter)
                    self.setData(index, result, TreeModel.ResultRole)
            else:
                shelters.append(child.name)
        
        # an empty list means that the calculation was successful
        if not shelters:
            self.calculated.emit()
        
        return shelters
コード例 #17
0
 def on_projectTree_doubleClicked(self, idx: QModelIndex):
     p = idx.data(Qt.UserRole).page
     if p is not None:
         self.openPage.emit(p)