コード例 #1
0
ファイル: main_window.py プロジェクト: rcy17/pybase
 def on_tree_file_doubleClicked(self, index: QModelIndex):
     if index.parent().data() == 'table':
         sql = f'USE {index.parent().parent().data()};SELECT * FROM {index.data()};'
         self.run_sql(sql)
     elif index.parent().data() == 'schema':
         sql = f'USE {index.parent().parent().data()};DESC {index.data()};'
         self.run_sql(sql)
コード例 #2
0
    def setData(self, idx: QModelIndex, value, role: int = Qt.EditRole):
        if not idx.isValid() or idx.column() != 1:
            return False

        if role == Qt.EditRole:
            if Id.depth(idx) == Id.Depth.D1 and idx.parent().row(
            ) == Row.ZEditOptions and isBoolOption(idx.row()):
                setattr(self.mergeFile, MERGE_OPTIONS[idx.row()][0],
                        value is True)
                self.dataChanged.emit(idx, idx, [role])
                return True

            if Id.depth(idx) == Id.Depth.D0:
                if idx.row() == Row.ModName:
                    self.mergeFile.name = value if value is not None else ""
                    self.dataChanged.emit(idx, idx, [role])
                    return True
                elif idx.row() == Row.PluginName:
                    self.mergeFile.filename = value if value is not None else ""
                    self.dataChanged.emit(idx, idx, [role])
                    return True
            elif Id.depth(idx) == Id.Depth.D1 and idx.parent().row(
            ) == Row.ZEditOptions:
                if idx.row() == OptionRow.Method:
                    if value in MergeFile.METHOD_VALUES:
                        self.mergeFile.method = value
                        self.dataChanged.emit(idx, idx, [role])
                        return True
                elif idx.row() == OptionRow.ArchiveAction:
                    if value in MergeFile.ARCHIVEACTION_VALUES:
                        self.mergeFile.archiveAction = value
                        self.dataChanged.emit(idx, idx, [role])
                        return True
        return False
コード例 #3
0
 def data(self, index: QtCore.QModelIndex, role: Qt = Qt.DisplayRole) -> typing.Any:
     result = None
     invalid_index = QtCore.QModelIndex()
     if index.isValid():
         node = index.internalPointer()
         if index.parent() == invalid_index:
             check_head: ChecklistItemHead = node.ref
             if role == Qt.DisplayRole:
                 if index.column() == 0:
                     result = check_head.name
                 elif index.column() == 1:
                     pass
                 else:
                     raise RuntimeError(f'Invalid column: {index.column()}')
             elif role == Qt.CheckStateRole and index.column() == 1:
                 result = check_head.validated
             elif role == Qt.BackgroundRole and index.column() == 1 and check_head.validated == Qt.Checked:
                 result = QtGui.QColor(Qt.green)
         else:  # it is a checklist property
             check_head: ChecklistItemHead = index.parent().internalPointer().ref
             check_property: ChecklistItemProperty = node.ref
             if role == Qt.DisplayRole:
                 if index.column() == 0:
                     result = check_property.name
                 elif index.column() == 1:
                     if index.row() == ChecklistItemPropertyColumn.AUTOMATION.value:
                         if check_head.automation is None:
                             result = 'Not enabled'
                         else:
                             pass
                     else:
                         result = check_property.value
                 else:
                     raise RuntimeError(f'Invalid column: {index.column()}')
     return result
コード例 #4
0
ファイル: MergeModel.py プロジェクト: mdavis199/MergeWizard
    def data(self, idx: QModelIndex, role: int = Qt.DisplayRole):
        depth = Id.depth(idx)
        if depth == Id.Depth.Invalid:
            return

        if depth == Id.Depth.D0:
            if role == Role.Data:
                return self.__merges[idx.row()]
            if role == Qt.DisplayRole:
                if idx.column() == Column.Name:
                    if idx.row() == 0:
                        return self.tr("-- New Merge --")
                    return self.__merges[idx.row()].name
                elif idx.column() == Column.PluginCount:
                    return self.rowCount(idx)
            elif role == Qt.ForegroundRole:
                if idx.column() == Column.PluginCount:
                    return QColor(Qt.lightGray).darker()
            elif role == Qt.FontRole:
                if idx.row() == 0:
                    font = QFont()
                    font.setBold(True)
                    return font
            elif role == Qt.DecorationRole:
                if idx.column() == Column.Active and idx.row() > 0:
                    if not self.__merges[idx.row()].modIsActive:
                        return QIcon(Icon.INACTIVE)

        if depth == Id.Depth.D1 and idx.column() == Column.Name:
            if role == Role.Data:
                return self.__merges[idx.parent().row()]
            if role == Qt.DisplayRole:
                return self.__merges[idx.parent().row()].plugins[idx.row()].filename
            if role == Qt.ForegroundRole:
                return QColor(Qt.lightGray).darker()
コード例 #5
0
 def _itemClicked(self, idx: QModelIndex):
     """ If the user clicks on the plugin, we choose its parent Merge.
     The view emits a selectionChanged signal. """
     if idx.parent().isValid():
         self.ui.mergeView.selectionModel().select(
             idx.parent(),
             QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
コード例 #6
0
 def onViewDoubleClicked(self, idx: QModelIndex):
     idx = self.infoModel().mapToSource(idx)
     if Id.depth(idx) == Id.Depth.D2:
         if idx.parent().row() == Row.MergedBy or idx.parent().row() == Row.MergedPlugins:
             name = self.infoModel().sourceModel().data(idx.siblingAtColumn(1))
             if name:
                 self.doubleClicked.emit(name)
コード例 #7
0
    def _display_data(self, idx: QModelIndex):
        depth = Id.depth(idx)

        if depth == Id.Depth.D0:
            if idx.column() == Column.Property:
                if idx.row() == Row.ModName:
                    return self.tr("Mod Name: ")
                if idx.row() == Row.PluginName:
                    return self.tr("Plugin Name: ")
                if idx.row() == Row.WhenBuilt:
                    return self.tr("Built On: ")
                if idx.row() == Row.ZEditOptions:
                    return self.tr("zMerge Options: ")
                if idx.row() == Row.LoadOrder:
                    return self.tr("Load Order: ")
                if idx.row() == Row.MergedPlugins:
                    return self.tr("Merged Plugins: ")

            elif idx.column() == Column.Value:
                if idx.row() == Row.ModName:
                    return self.mergeFile.name
                if idx.row() == Row.PluginName:
                    return self.mergeFile.filename
                if idx.row() == Row.WhenBuilt:
                    return self.mergeFile.dateBuilt
                if idx.row() == Row.ZEditOptions:
                    return self.tr("({})").format(len(MERGE_OPTIONS))
                if idx.row() == Row.LoadOrder:
                    return self.tr("({})").format(len(
                        self.mergeFile.loadOrder))
                if idx.row() == Row.MergedPlugins:
                    return self.tr("({})").format(len(self.mergeFile.plugins))

        elif depth == Id.Depth.D1:
            parentRow = idx.parent().row()
            if parentRow == Row.LoadOrder:
                if idx.column() == 0:
                    return "{}".format(idx.row() + 1)
                elif idx.column() == 1:
                    return self.mergeFile.loadOrder[idx.row()]
            elif parentRow == Row.MergedPlugins:
                if idx.column() == 0:
                    return "{}".format(idx.row() + 1)
                elif idx.column() == 1:
                    return self.mergeFile.plugins[idx.row()].filename
                elif idx.column() == 2:
                    return self.mergeFile.plugins[idx.row()].dataFolder
            if idx.parent().row() == Row.ZEditOptions:
                if idx.column() == 0:
                    return MERGE_OPTIONS[idx.row()][1]
                elif idx.column() == 1:
                    value = getattr(self.mergeFile,
                                    MERGE_OPTIONS[idx.row()][0], None)
                    if isinstance(value, bool):
                        return "true" if value else "false"
                    return value
コード例 #8
0
    def _display_data(self, idx: QModelIndex):
        depth = Id.depth(idx)

        if depth == Id.Depth.D0:
            if idx.column() == Column.Property:
                return self.tr("Plugin Name: ")
            if idx.column() == Column.Value:
                return self.data(idx, Role.Data).pluginName

        if depth == Id.Depth.D1:
            if idx.column() == Column.Property:
                if idx.row() == Row.MergedBy:
                    return self.tr("Merged By: ")
                if idx.row() == Row.MergedPlugins:
                    return self.tr("Merged Plugins: ")
                if idx.row() == Row.WhenBuilt:
                    return self.tr("Built On: ")
                if idx.row() == Row.ZEditOptions:
                    return self.tr("zMerge Options: ")

            if idx.column() == Column.Value:
                plugin = self.data(idx, Role.Data)
                plugins, merges = self.data(idx, Role.MergeAssociations)
                if idx.row() == Row.MergedBy and merges:
                    return self.tr("({})").format(len(merges))
                if idx.row() == Row.MergedPlugins and plugins:
                    return self.tr("({})").format(len(plugins))
                if idx.row() == Row.ZEditOptions and plugin.isMerge:
                    return self.tr("({})").format(len(MERGE_OPTIONS))
                if idx.row(
                ) == Row.WhenBuilt and plugin.isMerge and plugin.mergeFile:
                    return plugin.mergeFile.dateBuilt

        if depth == Id.Depth.D2:
            if idx.parent().row() == Row.MergedBy:
                if idx.column() == 0:
                    return "{}".format(idx.row() + 1)
                if idx.column() == 1:
                    plugins, merges = self.data(idx, Role.MergeAssociations)
                    return merges[idx.row()].pluginName
            if idx.parent().row() == Row.MergedPlugins:
                if idx.column() == 0:
                    return "{}".format(idx.row() + 1)
                if idx.column() == 1:
                    plugins, merges = self.data(idx, Role.MergeAssociations)
                    return plugins[idx.row()].pluginName
            if idx.parent().row() == Row.ZEditOptions:
                if idx.column() == 0:
                    return MERGE_OPTIONS[idx.row()][1]
                if idx.column() == 1:
                    plugin = self.data(idx, Role.Data)
                    return getattr(plugin.mergeFile,
                                   MERGE_OPTIONS[idx.row()][0], None)
コード例 #9
0
 def get_file_path(self, index: QModelIndex):
     """ From index to get real file path
     """
     if not index.isValid():
         return None
     path = list()
     path.append(index.data())
     while index.parent().isValid():
         index = index.parent()
         path.append(index.data())
     row = index.row()
     path[-1] = self.base_dirs[row]
     path.reverse()
     return '/'.join(path)
コード例 #10
0
    def on_clicked(self, index: QModelIndex):
        widgets_map['create_vault_button'].set_enabled(False)

        if index.parent().isValid():
            self.on_clicked_vault()
        else:
            self.on_clicked_region()
コード例 #11
0
 def background(self, index: QModelIndex):
     sourceParent = index.parent()
     dataRow = index.row()
     dataIndex = self.parent.index(dataRow, 1, sourceParent)  ## get ticker
     ticker = dataIndex.data()
     col = marker_background_color(self.dataObject, ticker)
     return col
コード例 #12
0
 def background(self, index: QModelIndex):
     sourceParent = index.parent()
     dataRow = index.row()
     dataIndex = self.parent.index(dataRow, 0, sourceParent)  ## get name
     name = dataIndex.data()
     ticker = self.dataObject.getTickerFromName(name)
     return stock_background_color(self.dataObject, ticker)
コード例 #13
0
    def data(self, idx: QModelIndex, role: int = Qt.DisplayRole):
        depth = Id.depth(idx)
        if depth == Id.Depth.Invalid:
            return

        if role == Role.Data:
            # return the top level plugin
            if depth == Id.Depth.D0:
                return self.sourcePlugin(idx.row())
            if depth == Id.Depth.D1:
                return self.sourcePlugin(Id.row(idx.internalId()))
            if depth == Id.Depth.D2:
                return self.sourcePlugin(Id.row(Id.parentId(idx.internalId())))

        if role == Role.MergeAssociations:
            if depth == Id.Depth.D0:
                return self.mergeAssociations(idx.row())
            if depth == Id.Depth.D1:
                return self.mergeAssociations(Id.row(idx.internalId()))
            if depth == Id.Depth.D2:
                return self.mergeAssociations(
                    Id.row(Id.parentId(idx.internalId())))

        if role == Role.Cell:
            return self._display_data(idx)
        if not idx.parent().isValid():
            return
        if role == Qt.DisplayRole:
            return self._display_data(idx)
        return self._style_data(idx, role)
コード例 #14
0
ファイル: custom_models.py プロジェクト: Wroppy/werry_math
 def filterAcceptsRow(self, source_row: int, source_parent: QModelIndex) -> bool:
     accept = super(CustomFilterModel, self).filterAcceptsRow(source_row, source_parent)
     if source_parent.row() == -1:
         return accept
     if accept:
         return True
     return self.filterAcceptsRow(source_parent.row(), source_parent.parent())
コード例 #15
0
ファイル: accounts.py プロジェクト: biqqles/wingman
 def onDataChanged(self, topLeft: QtCore.QModelIndex, bottomRight: QtCore.QModelIndex):
     """Handle row insertions. This method is actually called for every data change which is frustratingly
     inefficient. However insertRows doesn't work for this purpose as, as far as I can tell, before it returns
     rows are not actually added to the model making calling updateAccountSummary moot."""
     parent = topLeft.parent()
     if parent.isValid():
         self.updateAccountSummary(self.itemFromIndex(parent))
コード例 #16
0
    def visualRect(self, index: qtcore.QModelIndex) -> qtcore.QRect:
        rect = super(QPortfolioTableView, self).visualRect(index)
        if index.parent().isValid():  # child level
            if index.column() == 0:  # properties
                sectionStart = self.header().logicalIndex(0)
                sectionEnd = self.header().logicalIndex(2)
                left = self.header().sectionViewportPosition(sectionStart)
                right = self.header().sectionViewportPosition(sectionEnd) + self.header().sectionSize(sectionEnd)
                indent = 1 * self.indentation()
                left += indent
                # update visual rect
                rect.setX(int(left + 1))
                rect.setY(int(rect.y() + 1))
                rect.setWidth(right - left - 2)
                rect.setHeight(rect.height() - 2)

            if index.column() == 3:  # chart
                sectionStart = self.header().logicalIndex(3)
                sectionEnd = self.header().logicalIndex(self.header().count() - 1)
                left = self.header().sectionViewportPosition(sectionStart)
                right = self.header().sectionViewportPosition(sectionEnd) + self.header().sectionSize(sectionEnd)
                # update visual rect
                rect.setX(int(left + 1))
                rect.setY(int(rect.y() + 1))
                rect.setWidth(right - left - 2)
                rect.setHeight(rect.height() - 2)
        return rect
コード例 #17
0
ファイル: BookmarksDialog.py プロジェクト: pycom/EricShort
 def __newFolder(self):
     """
     Private slot to add a new bookmarks folder.
     """
     from .BookmarkNode import BookmarkNode
     
     currentIndex = self.bookmarksTree.currentIndex()
     idx = QModelIndex(currentIndex)
     sourceIndex = self.__proxyModel.mapToSource(idx)
     sourceNode = self.__bookmarksModel.node(sourceIndex)
     row = -1    # append new folder as the last item per default
     
     if sourceNode is not None and \
        sourceNode.type() != BookmarkNode.Folder:
         # If the selected item is not a folder, add a new folder to the
         # parent folder, but directly below the selected item.
         idx = idx.parent()
         row = currentIndex.row() + 1
     
     if not idx.isValid():
         # Select bookmarks menu as default.
         idx = self.__proxyModel.index(1, 0)
     
     idx = self.__proxyModel.mapToSource(idx)
     parent = self.__bookmarksModel.node(idx)
     node = BookmarkNode(BookmarkNode.Folder)
     node.title = self.tr("New Folder")
     self.__bookmarksManager.addBookmark(parent, node, row)
コード例 #18
0
 def __newFolder(self):
     """
     Private slot to add a new bookmarks folder.
     """
     from .BookmarkNode import BookmarkNode
     
     currentIndex = self.bookmarksTree.currentIndex()
     idx = QModelIndex(currentIndex)
     sourceIndex = self.__proxyModel.mapToSource(idx)
     sourceNode = self.__bookmarksModel.node(sourceIndex)
     row = -1    # append new folder as the last item per default
     
     if (
         sourceNode is not None and
         sourceNode.type() != BookmarkNode.Folder
     ):
         # If the selected item is not a folder, add a new folder to the
         # parent folder, but directly below the selected item.
         idx = idx.parent()
         row = currentIndex.row() + 1
     
     if not idx.isValid():
         # Select bookmarks menu as default.
         idx = self.__proxyModel.index(1, 0)
     
     idx = self.__proxyModel.mapToSource(idx)
     parent = self.__bookmarksModel.node(idx)
     node = BookmarkNode(BookmarkNode.Folder)
     node.title = self.tr("New Folder")
     self.__bookmarksManager.addBookmark(parent, node, row)
コード例 #19
0
    def sizeHint(self, option: QtWidgets.QStyleOptionViewItem,
                 index: QtCore.QModelIndex) -> QtCore.QSize:
        is_first_level = index.parent() == QtCore.QModelIndex()
        multiline_columns = (
            ChecklistItemPropertyColumn.DESCRIPTION.value,
            ChecklistItemPropertyColumn.GUIDE.value,
            ChecklistItemPropertyColumn.VALIDATION_NOTES.value,
        )
        is_multiline = index.row() in multiline_columns
        if not is_first_level and is_multiline and index.column() == 1:
            check_property: ChecklistItemProperty = index.internalPointer().ref
            text_to_draw = check_property.value
            base_width = self.gui_view.columnWidth(1)
            base_height = 10000  # some ridiculous high value just for initialization
            base_size = QtCore.QSize(base_width, base_height)

            metrics = QtGui.QFontMetrics(option.font)
            out_rect: QtCore.QRect = metrics.boundingRect(
                QtCore.QRect(QtCore.QPoint(0, 0), base_size),
                Qt.AlignLeft | Qt.AlignTop | Qt.TextWordWrap,
                text_to_draw,
            )
            base_size.setHeight(out_rect.height())
            result = base_size
        else:
            result = super().sizeHint(option, index)
        return result
コード例 #20
0
 def _tooltip_data(self, idx: QModelIndex):
     depth = Id.depth(idx)
     if depth == Id.Depth.D1 and idx.parent().row() == Row.MergedPlugins:
         dataFolder = self.mergeFile.plugins[idx.row()].dataFolder
         return '"{}"'.format(dataFolder)
         hash = getattr(self.mergeFile.plugins[idx.row()], "hash", "")
         return "<html><head/><body><p>folder: " + dataFolder + "</p><p>hash: " + hash + "</p></body></html>"
コード例 #21
0
    def insertRows(self,
                   node_type,
                   position: int,
                   rows: int,
                   parent: QModelIndex = QModelIndex()) -> bool:
        """
        :param parent: QModelIndex
        """
        parent_node = self.getNode(parent)
        parent_num_children = parent_node.childCount()

        parent = self.index(parent.row(), 0, parent.parent())

        if (position < 0 or parent_node.childCount() < position or rows < 0):
            return False

        self.beginInsertRows(parent, parent_num_children,
                             parent_num_children + rows - 1)

        for _ in range(rows):
            child_node = node_type(parent_node)
            success = parent_node.insertChild(position, child_node)

        self.endInsertRows()

        return success
コード例 #22
0
 def handle_parts_tree_activated(self, qindex: QModelIndex):
     if isinstance(qindex.internalPointer(), ArmorSetNode):
         return
     self.armor_item_mapper.setRootIndex(qindex.parent())
     self.armor_item_mapper.setCurrentModelIndex(qindex)
     entry = qindex.internalPointer().ref
     self.crafting_requirements_editor.set_current(entry.id)
コード例 #23
0
    def delete_plot(self, index: QModelIndex):
        logging.debug('Delete plot with index %s', index)

        if not index.isValid():
            return

        data = _get_data(index)
        parent = index.parent()
        if isinstance(data, _ParentData):
            # Notify view to remove all children plots
            self.beginRemoveRows(parent, index.row(), index.row())
            for child in data.children:
                self.visibility_changed(False, child.plot)
            self._files.remove(data)
            self.endRemoveRows()
        elif isinstance(data, _ChildData):
            # Get parent and remove parent from childre
            parent_data = _get_data(parent)
            if not isinstance(parent_data, _ParentData):
                _raise_data_err(parent_data)

            if index.row() >= len(parent_data.children):
                logging.warn('Selected index is out of range')
                return

            self.beginRemoveRows(parent, index.row(), index.row())
            self.visibility_changed(False, data.plot)
            parent_data.children.remove(data)
            self.endRemoveRows()
        else:
            _raise_data_err(data)
コード例 #24
0
    def _setData(self,
                 index: QModelIndex,
                 value: Any,
                 role: int = Qt.EditRole) -> bool:
        """Устанавливает локальные данные.

        _setData(self, index: QModelIndex, value: Any, role: int = Qt.EditRole) -> bool
        """
        assert self.checkIndex(index)

        # if role is None:
        #     role = Qt.EditRole

        if role == Vns.ItemDataRole.ItemDict:
            assert index.isValid()
            assert isinstance(value, dict)
            indexWithZeroColumn = self.sibling(index.row(), self.ZERO_COLUMN,
                                               index)
            if self.__localDataModel.setData(
                    self._mapToLocal(indexWithZeroColumn), value, role):
                indexWithLastColumn = self.sibling(
                    index.row(),
                    self.columnCount(index.parent()) - 1, index)
                self.dataChanged.emit(indexWithZeroColumn, indexWithLastColumn,
                                      [])
                return True
            return False
        elif role == Vns.ItemDataRole._ChildrenLoadingInfo:
            return False
        elif role == Vns.ItemDataRole.ChildrenAreLoadedSeparately:
            return False
        elif role == Vns.ItemDataRole.ChildrenLoadingPolicy:
            assert isinstance(value, Vns.LoadingPolicy)
            # TODO: Для какого индекса надо устанавливать политику: для index или для indexWithZeroColumn?
            if self.setChildrenLoadingPolicy(value, index):
                # TODO: Какие индексы должны быть в сигнале?
                self.dataChanged.emit(index, index, [role])
                return True
            return False
        elif role == Vns.ItemDataRole.ChildrenLoadingState:
            return False
        elif role == Vns.ItemDataRole.ChildrenPagination:
            return False
        elif role == Vns.ItemDataRole._DetailsLoadingInfo:
            return False
        elif role == Vns.ItemDataRole.DetailsAreLoadedSeparately:
            return False
        elif role == Vns.ItemDataRole.DetailsAreLoaded:
            return False
        elif role == Vns.ItemDataRole.DetailsLoadingState:
            return False

        assert not (Vns.ItemDataRole.First <= role < Vns.ItemDataRole.Custom)

        if role == Qt.DisplayRole or role == Qt.EditRole:
            return False

        return self.__localDataModel.setData(self._mapToLocal(index), value,
                                             role)
コード例 #25
0
 def _getBgColor(self, index: QModelIndex):
     bg = super(DetailedInfoTable, self)._getBgColor(index)
     if isinstance(bg, QBrush) and bg.color().alpha():
         return bg.color()
     color = LIGHT_BLUE
     if index.parent().isValid():
         if index.row() == 0:
             color.setAlpha(260 - index.parent().data(Qt.BackgroundRole).alpha())
         else:
             color.setAlpha(
                 260 - index.siblingAtRow(index.row() - 1).data(Qt.BackgroundRole).alpha())
     else:
         if index.row() % 2:
             color.setAlpha(150)
         else:
             color.setAlpha(110)
     return color
コード例 #26
0
ファイル: favswidget.py プロジェクト: anetczuk/stock-monitor
 def background(self, index: QModelIndex ):
     sourceParent = index.parent()
     dataRow = index.row()
     dataIndex = self.parent.index( dataRow, 3, sourceParent )       ## get ticker
     ticker = dataIndex.data()
     markerColor = marker_background_color( self.dataObject, ticker )
     if markerColor is not None:
         return markerColor
     return wallet_background_color( self.dataObject, ticker )
コード例 #27
0
 def sectionSizeFromContents(self, logicalIndex: int) -> QSize:
     if self._pd.headerModel:
         curLeafIndex = QModelIndex(self._pd.leafIndex(logicalIndex))
         if curLeafIndex.isValid():
             styleOption = QStyleOptionHeader(
                 self.styleOptionForCell(logicalIndex))
             s = QSize(self._pd.cellSize(curLeafIndex, self, styleOption))
             curLeafIndex = curLeafIndex.parent()
             while curLeafIndex.isValid():
                 if self.orientation() == Qt.Horizontal:
                     s.setHeight(s.height() + self._pd.cellSize(
                         curLeafIndex, self, styleOption).height())
                 else:
                     s.setWidth(s.width() + self._pd.cellSize(
                         curLeafIndex, self, styleOption).width())
                 curLeafIndex = curLeafIndex.parent()
             return s
     return super().sectionSizeFromContents(logicalIndex)
コード例 #28
0
 def flags(self, index: QtCore.QModelIndex) -> QtCore.Qt.ItemFlags:
     result = super().flags(index)
     if index.isValid():
         if index.parent() == QtCore.QModelIndex():
             if index.column() == 1:
                 result = result | Qt.ItemIsEditable | Qt.ItemIsUserCheckable
         else:
             if index.row() == ChecklistItemPropertyColumn.VALIDATION_NOTES.value and index.column() == 1:
                 result = result | Qt.ItemIsEditable
     return result
コード例 #29
0
 def flags(self, idx: QModelIndex):
     if self._displayCheckbox and idx.isValid(
     ) and not idx.parent().isValid():
         sourceIdx = self.mapToSource(idx)
         flags = self.sourceModel().flags(sourceIdx)
         if sourceIdx.column() == Column.PluginName:
             return flags | Qt.ItemIsUserCheckable
     if self._readonly:
         return super().flags(idx) & ~Qt.ItemIsEditable
     return super().flags(idx)
コード例 #30
0
ファイル: HookManager.py プロジェクト: Xkein/yr-tools
    def clvHooksClicked(self, index: QModelIndex):
        if index.parent().column() != 0:
            return

        data = index.data()
        model = mainWindowHelper.tbvAllHooks.model()
        match = model.match(model.index(0, 0), Qt.DisplayRole,
                            data, 1, Qt.MatchContains)
        if len(match) > 0:
            mainWindowHelper.tbvAllHooks.selectRow(match[0].row())
コード例 #31
0
 def _checkstate_data(self, idx: QModelIndex):
     return
     depth = Id.depth(idx)
     if (depth == Id.Depth.D1 and idx.column() == 1
             and idx.parent().row() == Row.ZEditOptions
             and isBoolOption(idx.row())):
         if getattr(self.mergeFile, MERGE_OPTIONS[idx.row()][0], False):
             return Qt.Checked
         else:
             return Qt.Unchecked
コード例 #32
0
ファイル: folders_treeview.py プロジェクト: nuxeo/nuxeo-drive
 def expand_item(self, index: QModelIndex) -> None:
     index = self.model().index(index.row(), 0, index.parent())
     item = self.model().itemFromIndex(index)
     self.load_children(item)
コード例 #33
0
class textEditView(QTextEdit):
    def __init__(self, parent=None, index=None, html=None, spellcheck=True, highlighting=False, dict="",
                 autoResize=False):
        QTextEdit.__init__(self, parent)
        self._column = Outline.text.value
        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.spellcheck = spellcheck
        self.currentDict = dict if dict else settings.dict
        self.highlighter = None
        self.setAutoResize(autoResize)
        self._defaultBlockFormat = QTextBlockFormat()
        self._defaultCharFormat = QTextCharFormat()
        self.highlightWord = ""
        self.highligtCS = False
        self.defaultFontPointSize = qApp.font().pointSize()
        self._dict = None
        # self.document().contentsChanged.connect(self.submit, 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, 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 enchant.get_default_language())
            except enchant.errors.DictNotFoundError:
                self.spellcheck = False

        else:
            self.spellcheck = False

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

    def setModel(self, model):
        self._model = model
        try:
            self._model.dataChanged.connect(self.update, AUC)
        except TypeError:
            pass
        try:
            self._model.rowsAboutToBeRemoved.connect(self.rowsAboutToBeRemoved, 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 = 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 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(i)

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

        self.updateText()

    def setupEditorForIndex(self, index):
        # In which model are we editing?
        if type(index.model()) != outlineModel:
            self._textFormat = "text"
            return

        # what type of text are we editing?
        if self._column not in [Outline.text.value, Outline.notes.value]:
            self._textFormat = "text"

        else:
            self._textFormat = "md"

        # Setting highlighter
        if self._highlighting:
            item = index.internalPointer()
            if self._column in [Outline.text.value, Outline.notes.value]:
                self.highlighter = MMDHighlighter(self)
            else:
                self.highlighter = basicHighlighter(self)

            self.highlighter.setDefaultBlockFormat(self._defaultBlockFormat)

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

        opt = settings.textEditor
        f = QFont()
        f.fromString(opt["font"])
        # self.setFont(f)
        self.setStyleSheet("""QTextEdit{{
            background: {bg};
            color: {foreground};
            font-family: {ff};
            font-size: {fs};
            }}
            """.format(
                bg=opt["background"],
                foreground=opt["fontColor"],
                ff=f.family(),
                fs="{}pt".format(str(f.pointSize()))))

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

        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"])

        self._defaultCharFormat = cf
        self._defaultBlockFormat = bf

        if self.highlighter:
            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

        elif self._index:

            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 rowsAboutToBeRemoved(self, parent, first, last):
        if self._index:
            if self._index.parent() == parent and \
                                    first <= self._index.row() <= last:
                self._index = None
                self.setEnabled(False)

                # FIXME: self._indexes

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

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

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

        elif self._indexes:
            self.disconnectDocument()
            t = []
            same = True
            for i in self._indexes:
                item = i.internalPointer()
                t.append(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:
            # item = self._index.internalPointer()
            if self.toPlainText() != self._model.data(self._index):
                # print("    Submitting plain text")
                self._updating = True
                self._model.setData(self._index, self.toPlainText())
                self._updating = False

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

    def keyPressEvent(self, event):
        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):
        docHeight = self.document().size().height()
        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
        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:
                self._dict = enchant.Dict(self.currentDict)
            elif 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)

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

        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 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)
                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.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.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()

    def focusInEvent(self, event):
        """Finds textFormatter and attach them to that view."""
        QTextEdit.focusInEvent(self, event)

        p = self.parent()
        while p.parent():
            p = p.parent()

        if self._index:
            for tF in p.findChildren(textFormat, QRegExp(".*"), Qt.FindChildrenRecursively):
                tF.updateFromIndex(self._index)
                tF.setTextEdit(self)

    def applyFormat(self, _format):

        if self._textFormat == "md":

            if _format == "Bold":
                MDFormatSelection(self, 0)
            elif _format == "Italic":
                MDFormatSelection(self, 1)
            elif _format == "Code":
                MDFormatSelection(self, 2)
            elif _format == "Clear":
                MDFormatSelection(self)
コード例 #34
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
コード例 #35
0
ファイル: editorWidget.py プロジェクト: georgehank/manuskript
class editorWidget(QWidget, Ui_editorWidget_ui):
    toggledSpellcheck = pyqtSignal(bool)
    dictChanged = pyqtSignal(str)

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        self.setupUi(self)
        self.currentIndex = QModelIndex()
        self.currentID = None
        self.txtEdits = []
        self.scroll.setBackgroundRole(QPalette.Base)
        self.toggledSpellcheck.connect(self.txtRedacText.toggleSpellcheck, AUC)
        self.dictChanged.connect(self.txtRedacText.setDict, AUC)
        self.txtRedacText.setHighlighting(True)
        self.currentDict = ""
        self.spellcheck = True
        self.folderView = "cork"
        self.mw = mainWindow()

        # def setModel(self, model):
        # self._model = model
        # self.setView()

    def setFolderView(self, v):
        oldV = self.folderView
        if v == "cork":
            self.folderView = "cork"
        elif v == "outline":
            self.folderView = "outline"
        else:
            self.folderView = "text"

        # Saving value
        settings.folderView = self.folderView

        if oldV != self.folderView and self.currentIndex:
            self.setCurrentModelIndex(self.currentIndex)

    def setCorkSizeFactor(self, v):
        self.corkView.itemDelegate().setCorkSizeFactor(v)
        self.redrawCorkItems()

    def redrawCorkItems(self):
        r = self.corkView.rootIndex()

        if r.isValid():
            count = r.internalPointer().childCount()
        else:
            count = self.mw.mdlOutline.rootItem.childCount()

        for c in range(count):
            self.corkView.itemDelegate().sizeHintChanged.emit(r.child(c, 0))

    def setView(self):
        # index = mainWindow().treeRedacOutline.currentIndex()

        # Couting the number of other selected items
        # sel = []
        # for i in mainWindow().treeRedacOutline.selectionModel().selection().indexes():
        # if i.column() != 0: continue
        # if i not in sel: sel.append(i)

        # if len(sel) != 0:
        # item = index.internalPointer()
        # else:
        # index = QModelIndex()
        # item = self.mw.mdlOutline.rootItem

        # self.currentIndex = index

        if self.currentIndex.isValid():
            item = self.currentIndex.internalPointer()
        else:
            item = self.mw.mdlOutline.rootItem

        def addTitle(itm):
            edt = textEditView(self, html="<h{l}>{t}</h{l}>".format(l=min(itm.level() + 1, 5), t=itm.title()),
                               autoResize=True)
            edt.setFrameShape(QFrame.NoFrame)
            self.txtEdits.append(edt)
            l.addWidget(edt)

        def addLine():
            line = QFrame(self.text)
            line.setFrameShape(QFrame.HLine)
            line.setFrameShadow(QFrame.Sunken)
            l.addWidget(line)

        def addText(itm):
            edt = textEditView(self,
                               index=itm.index(),
                               spellcheck=self.spellcheck,
                               dict=settings.dict,
                               highlighting=True,
                               autoResize=True)
            edt.setFrameShape(QFrame.NoFrame)
            edt.setStyleSheet("background: {};".format(settings.textEditor["background"]))
            edt.setStatusTip("{} ({})".format(itm.path(), itm.type()))
            self.toggledSpellcheck.connect(edt.toggleSpellcheck, AUC)
            self.dictChanged.connect(edt.setDict, AUC)
            # edt.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
            self.txtEdits.append(edt)
            l.addWidget(edt)

        def addChildren(itm):
            for c in range(itm.childCount()):
                child = itm.child(c)

                if child.isFolder():
                    addTitle(child)
                    addChildren(child)

                else:
                    addText(child)
                    addLine()

        def addSpacer():
            l.addItem(QSpacerItem(10, 1000, QSizePolicy.Minimum, QSizePolicy.Expanding))

            # Display multiple selected items
            # if len(sel) > 1 and False:  # Buggy and not very useful, skip
            # self.stack.setCurrentIndex(1)
            # w = QWidget()
            # l = QVBoxLayout(w)
            # self.txtEdits = []
            # for idx in sel:
            # sItem = idx.internalPointer()
            # addTitle(sItem)
            # if sItem.isFolder():
            # addChildren(sItem)
            # else:
            # addText(sItem)
            # addLine()
            # addSpacer()
            # self.scroll.setWidget(w)

        if item and item.isFolder() and self.folderView == "text":
            self.stack.setCurrentIndex(1)
            w = QWidget()
            l = QVBoxLayout(w)
            w.setStyleSheet("background: {};".format(settings.textEditor["background"]))
            # self.scroll.setWidgetResizable(False)

            self.txtEdits = []

            if item != self.mw.mdlOutline.rootItem:
                addTitle(item)

            addChildren(item)
            addSpacer()
            self.scroll.setWidget(w)

        elif item and item.isFolder() and self.folderView == "cork":
            self.stack.setCurrentIndex(2)
            self.corkView.setModel(self.mw.mdlOutline)
            self.corkView.setRootIndex(self.currentIndex)
            self.corkView.selectionModel().selectionChanged.connect(
                    lambda: mainWindow().redacMetadata.selectionChanged(self.corkView), AUC)
            self.corkView.clicked.connect(
                    lambda: mainWindow().redacMetadata.selectionChanged(self.corkView), AUC)

        elif item and item.isFolder() and self.folderView == "outline":
            self.stack.setCurrentIndex(3)
            self.outlineView.setModelPersos(mainWindow().mdlPersos)
            self.outlineView.setModelLabels(mainWindow().mdlLabels)
            self.outlineView.setModelStatus(mainWindow().mdlStatus)
            self.outlineView.setModel(self.mw.mdlOutline)
            self.outlineView.setRootIndex(self.currentIndex)
            self.outlineView.selectionModel().selectionChanged.connect(
                    lambda: mainWindow().redacMetadata.selectionChanged(self.outlineView), AUC)
            self.outlineView.clicked.connect(
                    lambda: mainWindow().redacMetadata.selectionChanged(self.outlineView), AUC)

        else:
            self.txtRedacText.setCurrentModelIndex(self.currentIndex)
            self.stack.setCurrentIndex(0)  # Single text item

        try:
            self.mw.mdlOutline.dataChanged.connect(self.modelDataChanged, AUC)
            self.mw.mdlOutline.rowsInserted.connect(self.updateIndexFromID, AUC)
            self.mw.mdlOutline.rowsRemoved.connect(self.updateIndexFromID, AUC)
            self.mw.mdlOutline.rowsAboutToBeRemoved.connect(self.rowsAboutToBeRemoved, AUC)
        except TypeError:
            pass

        self.updateStatusBar()

    def setCurrentModelIndex(self, index=None):
        if index.isValid():
            self.currentIndex = index
            self.currentID = self.mw.mdlOutline.ID(index)
            # self._model = index.model()
        else:
            self.currentIndex = QModelIndex()

        self.setView()

    def updateIndexFromID(self):
        idx = self.mw.mdlOutline.getIndexByID(self.currentID)
        if idx != self.currentIndex:
            self.currentIndex = idx
            self.setView()

    def modelDataChanged(self, topLeft, bottomRight):
        # if self.currentID:
        # self.updateIndexFromID()
        if not self.currentIndex:
            return
        if topLeft.row() <= self.currentIndex.row() <= bottomRight.row():
            self.updateStatusBar()

    def rowsAboutToBeRemoved(self, parent, first, last):
        if self.currentIndex:
            if self.currentIndex.parent() == parent and \
                                    first <= self.currentIndex.row() <= last:
                # Item deleted, close tab
                self.mw.mainEditor.tab.removeTab(self.mw.mainEditor.tab.indexOf(self))

    def updateStatusBar(self):
        # Update progress
        # if self.currentIndex and self.currentIndex.isValid():
        # if self._model:
        mw = mainWindow()
        if not mw:
            return

        mw.mainEditor.updateStats()

    def toggleSpellcheck(self, v):
        self.spellcheck = v
        self.toggledSpellcheck.emit(v)

    def setDict(self, dct):
        self.currentDict = dct
        self.dictChanged.emit(dct)
コード例 #36
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")
コード例 #37
0
ファイル: editorWidget.py プロジェクト: olivierkes/manuskript
class editorWidget(QWidget, Ui_editorWidget_ui):
    """
    `editorWidget` is a class responsible for displaying and editing one
    `outlineItem`. This item can be a folder or a text.

    It has four views (see `self.setView`)

      - For folders: "text", "outline" or "cork" (set in `self.folderView`)

        Text: displays a list of `textEditView` in a scroll area

        Outline: displays an outline, using an `outlineView`

        Cork: displays flash cards, using a `corkView`

      - For text: item is simply displayed in a `textEditView`

    All those views are contained in `editorWidget` single widget: `self.stack`.

    `editorWidget` are managed in `tabSplitted` (that allow to open several
    `outlineItem`s, either in Tabs or in split views.

    `tabSplitted` are in turn managed by the `mainEditor`, which is unique and
    gives UI buttons to manage all those views.
    """

    toggledSpellcheck = pyqtSignal(bool)
    dictChanged = pyqtSignal(str)

    _maxTabTitleLength = 24

    def __init__(self, parent):
        QWidget.__init__(self, parent)
        self.setupUi(self)
        self.currentIndex = QModelIndex()
        self.currentID = None
        self.txtEdits = []
        self.scroll.setBackgroundRole(QPalette.Base)
        self.toggledSpellcheck.connect(self.txtRedacText.toggleSpellcheck, AUC)
        self.dictChanged.connect(self.txtRedacText.setDict, AUC)
        self.txtRedacText.setHighlighting(True)
        self.currentDict = ""
        self.spellcheck = settings.spellcheck
        self.folderView = "cork"
        self.mw = mainWindow()
        self._tabWidget = None  # set by mainEditor on creation

        self._model = None

        # Capture textEdit scrollbar, so that we can put it outside the margins.
        self.txtEditScrollBar = self.txtRedacText.verticalScrollBar()
        self.txtEditScrollBar.setParent(self)
        self.stack.currentChanged.connect(self.setScrollBarVisibility)

        # def setModel(self, model):
        # self._model = model
        # self.setView()

    def resizeEvent(self, event):
        """
        textEdit's scrollBar has been reparented to self. So we need to
        update it's geomtry when self is resized, and put it where we want it
        to be.
        """
        # Update scrollbar geometry
        r = self.geometry()
        w = 10  # Cf. style.mainEditorTabSS
        r.setWidth(w)
        r.moveRight(self.geometry().width())
        self.txtEditScrollBar.setGeometry(r)

        QWidget.resizeEvent(self, event)

    def setScrollBarVisibility(self):
        """
        Since the texteEdit scrollBar has been reparented to self, it is not
        hidden when stack changes. We have to do it manually.
        """
        self.txtEditScrollBar.setVisible(self.stack.currentIndex() == 0)

    def setFolderView(self, v):
        oldV = self.folderView
        if v == "cork":
            self.folderView = "cork"
        elif v == "outline":
            self.folderView = "outline"
        else:
            self.folderView = "text"

        # Saving value
        settings.folderView = self.folderView

        if oldV != self.folderView and self.currentIndex:
            self.setCurrentModelIndex(self.currentIndex)

    def setCorkSizeFactor(self, v):
        self.corkView.itemDelegate().setCorkSizeFactor(v)
        self.redrawCorkItems()

    def redrawCorkItems(self):
        r = self.corkView.rootIndex()

        if r.isValid():
            count = r.internalPointer().childCount()
        elif self._model:
            count = self._model.rootItem.childCount()
        else:
            count = 0

        for c in range(count):
            self.corkView.itemDelegate().sizeHintChanged.emit(r.child(c, 0))

    def updateTabTitle(self):
        """
        `editorWidget` belongs to a `QTabWidget` in a `tabSplitter`. We update
        the tab title to reflect that of current item.
        """
        # `self._tabWidget` is set by mainEditor when creating tab and `editorWidget`.
        # if `editorWidget` is ever used out of `mainEditor`, this could throw
        # an error.
        if not self._tabWidget:
            return

        if self.currentIndex.isValid():
            item = self.currentIndex.internalPointer()
        elif self._model:
            item = self._model.rootItem
        else:
            return

        i = self._tabWidget.indexOf(self)

        self._tabWidget.setTabText(i, self.ellidedTitle(item.title()))
        self._tabWidget.setTabToolTip(i, item.title())

    def ellidedTitle(self, title):
        if len(title) > self._maxTabTitleLength:
            return "{}…".format(title[:self._maxTabTitleLength])
        else:
            return title

    def setView(self):
        # index = mainWindow().treeRedacOutline.currentIndex()

        # Counting the number of other selected items
        # sel = []
        # for i in mainWindow().treeRedacOutline.selectionModel().selection().indexes():
        # if i.column() != 0: continue
        # if i not in sel: sel.append(i)

        # if len(sel) != 0:
        # item = index.internalPointer()
        # else:
        # index = QModelIndex()
        # item = self.mw.mdlOutline.rootItem

        # self.currentIndex = index

        if self.currentIndex.isValid():
            item = self.currentIndex.internalPointer()
        else:
            item = self.mw.mdlOutline.rootItem

        self.updateTabTitle()

        def addTitle(itm):
            edt = MDEditView(self, html="<h{l}>{t}</h{l}>".format(l=min(itm.level() + 1, 5), t=itm.title()),
                               autoResize=True)
            edt.setFrameShape(QFrame.NoFrame)
            self.txtEdits.append(edt)
            l.addWidget(edt)

        def addLine():
            line = QFrame(self.text)
            line.setFrameShape(QFrame.HLine)
            line.setFrameShadow(QFrame.Sunken)
            l.addWidget(line)

        def addText(itm):
            edt = MDEditView(self,
                               index=itm.index(),
                               spellcheck=self.spellcheck,
                               dict=settings.dict,
                               highlighting=True,
                               autoResize=True)
            edt.setFrameShape(QFrame.NoFrame)
            edt.setStatusTip("{}".format(itm.path()))
            self.toggledSpellcheck.connect(edt.toggleSpellcheck, AUC)
            self.dictChanged.connect(edt.setDict, AUC)
            # edt.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
            self.txtEdits.append(edt)
            l.addWidget(edt)

        def addChildren(itm):
            for c in range(itm.childCount()):
                child = itm.child(c)

                if child.isFolder():
                    addTitle(child)
                    addChildren(child)

                else:
                    addText(child)
                    addLine()

        def addSpacer():
            l.addItem(QSpacerItem(10, 1000, QSizePolicy.Minimum, QSizePolicy.Expanding))

            # Display multiple selected items
            # if len(sel) > 1 and False:  # Buggy and not very useful, skip
            # self.stack.setCurrentIndex(1)
            # w = QWidget()
            # l = QVBoxLayout(w)
            # self.txtEdits = []
            # for idx in sel:
            # sItem = idx.internalPointer()
            # addTitle(sItem)
            # if sItem.isFolder():
            # addChildren(sItem)
            # else:
            # addText(sItem)
            # addLine()
            # addSpacer()
            # self.scroll.setWidget(w)

        if item and item.isFolder() and self.folderView == "text":
            self.stack.setCurrentIndex(1)
            w = QWidget()
            w.setObjectName("editorWidgetFolderText")
            l = QVBoxLayout(w)
            opt = settings.textEditor
            background = (opt["background"] if not opt["backgroundTransparent"]
                          else "transparent")
            w.setStyleSheet("background: {};".format(background))
            self.stack.widget(1).setStyleSheet("background: {}"
                                               .format(background))
            # self.scroll.setWidgetResizable(False)

            self.txtEdits = []

            if item != self._model.rootItem:
                addTitle(item)

            addChildren(item)
            addSpacer()
            self.scroll.setWidget(w)

        elif item and item.isFolder() and self.folderView == "cork":
            self.stack.setCurrentIndex(2)
            self.corkView.setModel(self._model)
            self.corkView.setRootIndex(self.currentIndex)
            try:
                self.corkView.selectionModel().selectionChanged.connect(mainWindow().redacMetadata.selectionChanged, AUC)
                self.corkView.clicked.connect(mainWindow().redacMetadata.selectionChanged, AUC)
                self.corkView.clicked.connect(mainWindow().mainEditor.updateTargets, AUC)
            except TypeError:
                pass

        elif item and item.isFolder() and self.folderView == "outline":
            self.stack.setCurrentIndex(3)
            self.outlineView.setModelCharacters(mainWindow().mdlCharacter)
            self.outlineView.setModelLabels(mainWindow().mdlLabels)
            self.outlineView.setModelStatus(mainWindow().mdlStatus)
            self.outlineView.setModel(self._model)
            self.outlineView.setRootIndex(self.currentIndex)

            try:
                self.outlineView.selectionModel().selectionChanged.connect(mainWindow().redacMetadata.selectionChanged, AUC)
                self.outlineView.clicked.connect(mainWindow().redacMetadata.selectionChanged, AUC)
                self.outlineView.clicked.connect(mainWindow().mainEditor.updateTargets, AUC)
            except TypeError:
                pass

        if item and item.isText():
            self.txtRedacText.setCurrentModelIndex(self.currentIndex)
            self.stack.setCurrentIndex(0)  # Single text item
        else:
            self.txtRedacText.setCurrentModelIndex(QModelIndex())

        try:
            self._model.dataChanged.connect(self.modelDataChanged, AUC)
            self._model.rowsInserted.connect(self.updateIndexFromID, AUC)
            self._model.rowsRemoved.connect(self.updateIndexFromID, AUC)
            #self.mw.mdlOutline.rowsAboutToBeRemoved.connect(self.rowsAboutToBeRemoved, AUC)
        except TypeError:
            pass

        self.updateStatusBar()

    def setCurrentModelIndex(self, index=None):
        if index.isValid():
            self.currentIndex = index
            self._model = index.model()
            self.currentID = self._model.ID(index)
        else:
            self.currentIndex = QModelIndex()
            self.currentID = None

        if self._model:
            self.setView()

    def updateIndexFromID(self):
        """
        Index might have changed (through drag an drop), so we keep current
        item's ID and update index. Item might have been deleted too.
        """
        idx = self._model.getIndexByID(self.currentID)

        # If we have an ID but the ID does not exist, it has been deleted
        if self.currentID and idx == QModelIndex():
            # Item has been deleted, we open the parent instead
            self.setCurrentModelIndex(self.currentIndex.parent())
            # FIXME: selection in self.mw.treeRedacOutline is not updated
            #        but we cannot simply setCurrentIndex through treeRedacOutline
            #        because this might be a tab in the background / out of focus
            #        Also the UI of mainEditor is not updated (so the folder icons
            #        are not display, button "up" doesn't work, etc.).

        # Item has been moved
        elif idx != self.currentIndex:
            # We update the index
            self.currentIndex = idx
            self.setView()

    def modelDataChanged(self, topLeft, bottomRight):
        # if self.currentID:
        # self.updateIndexFromID()
        if not self.currentIndex:
            return
        if topLeft.row() <= self.currentIndex.row() <= bottomRight.row():
            self.updateStatusBar()

    #def rowsAboutToBeRemoved(self, parent, first, last):
        #if self.currentIndex:
            #if self.currentIndex.parent() == parent and \
                                    #first <= self.currentIndex.row() <= last:
                ## Item deleted, close tab
                #self.mw.mainEditor.tab.removeTab(self.mw.mainEditor.tab.indexOf(self))

    def updateStatusBar(self):
        # Update progress
        # if self.currentIndex and self.currentIndex.isValid():
        # if self._model:
        mw = mainWindow()
        if not mw:
            return

        mw.mainEditor.updateStats()

    def toggleSpellcheck(self, v):
        self.spellcheck = v
        self.toggledSpellcheck.emit(v)

    def setDict(self, dct):
        self.currentDict = dct
        self.dictChanged.emit(dct)

    ###############################################################################
    # FUNCTIONS FOR MENU ACCESS
    ###############################################################################

    def getCurrentItemView(self):
        """
        Returns the current item view, between txtRedacText, outlineView and
        corkView. If folder/text view, returns None. (Because handled
        differently)
        """

        if self.stack.currentIndex() == 0:
            return self.txtRedacText
        elif self.folderView == "outline":
            return self.outlineView
        elif self.folderView == "cork":
            return self.corkView
        else:
            return None

    def copy(self):
        if self.getCurrentItemView(): self.getCurrentItemView().copy()
    def cut(self):
        if self.getCurrentItemView(): self.getCurrentItemView().cut()
    def paste(self):
        if self.getCurrentItemView(): self.getCurrentItemView().paste()
    def rename(self):
        if self.getCurrentItemView(): self.getCurrentItemView().rename()
    def duplicate(self):
        if self.getCurrentItemView(): self.getCurrentItemView().duplicate()
    def delete(self):
        if self.getCurrentItemView(): self.getCurrentItemView().delete()
    def moveUp(self):
        if self.getCurrentItemView(): self.getCurrentItemView().moveUp()
    def moveDown(self):
        if self.getCurrentItemView(): self.getCurrentItemView().moveDown()

    def splitDialog(self):
        """
        Opens a dialog to split selected items.
        """
        if self.getCurrentItemView() == self.txtRedacText:
            # Text editor
            if not self.currentIndex.isValid():
                return

            sel = self.txtRedacText.textCursor().selectedText()
            # selectedText uses \u2029 instead of \n, no idea why.
            sel = sel.replace("\u2029", "\n")
            splitDialog(self, [self.currentIndex], mark=sel)

        elif self.getCurrentItemView():
            # One of the views
            self.getCurrentItemView().splitDialog()

    def splitCursor(self):
        """
        Splits items at cursor position. If there is a selection, that selection
        becomes the new item's title.

        Call context: Only works when editing a file.
        """

        if not self.currentIndex.isValid():
            return

        if self.getCurrentItemView() == self.txtRedacText:
            c = self.txtRedacText.textCursor()

            title = c.selectedText()
            # selection can be backward
            pos = min(c.selectionStart(), c.selectionEnd())

            item = self.currentIndex.internalPointer()

            item.splitAt(pos, len(title))

    def merge(self):
        """
        Merges selected items together.

        Call context: Multiple selection, same parent.
        """
        if self.getCurrentItemView() == self.txtRedacText:
            # Text editor, nothing to merge
            pass

        elif self.getCurrentItemView():
            # One of the views
            self.getCurrentItemView().merge()