コード例 #1
0
class ListModel(QAbstractTableModel):
    orderChanged = pyqtSignal()
    elementSelected = pyqtSignal(int)

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

        """

        ncols = 2
        Name = 0
        Delete = 1

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

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

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

        def onSelectionChanged(selected, deselected):

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

        self._selectionModel.selectionChanged.connect(onSelectionChanged)

        self._allowRemove = True
        self._toolTipSuffixes = {}

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

    def makeRowPermanent(self, rowIndex):
        """
        The rowindex cannot be removed from gui
        to remove this index use self.makeRowRemovable
        """

        self.unremovable_rows.append(rowIndex)

    def makeRowRemovable(self, rowIndex):
        """
        :param rowIndex: is the index for the label of interest in the current gui setting
        """
        self.unremovable_rows.remove(rowIndex)

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

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

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

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

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

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

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

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

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

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

        """
        def __init__(self, table, row):
            self._row = row
            self._table = table

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

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

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

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

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

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

    def data(self, index, role):
        """
        Reimplement, see labelListModel or boxListModel for concrete example
        :param index:
        :param role:
        """

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

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

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

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

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

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

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

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

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

        return False

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

    def clearSelectionModel(self):
        self._selectionModel.clear()
コード例 #2
0
class SMSTreeView(QTreeView):
    '''
    自定义树控件
    '''
    def __init__(self, parent=None):
        QTreeView.__init__(self, parent)
        #model
        self._model = CategoriesTreeModel(self)
        self.setModel(self._model)
        # selectionMode
        self._selection_model = QItemSelectionModel(self._model, self)
        self.setSelectionModel(self._selection_model)
        #delegate
        self._delegate = CategoriesTreeDelegate(parent)
        self.setItemDelegate(self._delegate)
        #阻止双击时出现编辑框
        self.setEditTriggers(self.NoEditTriggers)
        #设置展开/收缩动画
        self.setAnimated(True)
        #展开所有
        self.expandAll()
        #开启右键自定义菜单
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        #右键菜单信号槽
        self.customContextMenuRequested.connect(self._slot_custom_context_menu)
        #右键菜单项
        self._context_menu_add_child = QAction('添加子分类')
        self._context_menu_add_child.triggered.connect(
            self._slot_context_menu_add_child)
        self._context_menu_rename = QAction('重命名')
        self._context_menu_rename.triggered.connect(
            self._slot_context_menu_rename)
        self._context_menu_delete = QAction('删除该分类')
        self._context_menu_delete.triggered.connect(
            self._slot_context_menu_delete)
        # 设置默认选择为 root
        self._selection_model.select(self.rootIndex(),
                                     QItemSelectionModel.SelectCurrent)

    def _slot_custom_context_menu(self, point):
        menu = QMenu()
        current_index = self.currentIndex()
        current_item = current_index.internalPointer()
        #添加子类 菜单项
        menu.addAction(self._context_menu_add_child)
        #重命名 菜单项 和 删除 菜单项
        if current_item.parent != 0:
            menu.addAction(self._context_menu_rename)
            menu.addAction(self._context_menu_delete)

        menu.exec(QCursor.pos())

    def _slot_context_menu_rename(self, checked):
        self.edit(self.currentIndex())

    def _slot_context_menu_add_child(self, checked):
        current_index = self.currentIndex()
        result = QInputDialog.getText(self, '添加新分类', '请输入分类名', text='新分类')
        if result[1]:
            if not result[0]:
                QMessageBox.critical(self, '添加失败', '分类名不能为空')
            else:
                self._model.add_item(result[0], current_index)

    # 2017.2.11 sqlalchemy 一执行删除就挂了,不晓得为啥子
    def _slot_context_menu_delete(self, checked):
        QMessageBox.information(self, '功能暂时不可用', 'sqlalchemy 一执行删除就挂了,不晓得为啥子')
        return 0
        current_index = self.currentIndex()
        if self._model.has_child(current_index):
            if QMessageBox.question(self, '确认', \
            '该分类下还有子分类,删除该分类将连带删除子分类,确定删除?') \
            != QMessageBox.Yes:
                return 0
        self._model.remove_item(current_index)

    def set_select_changed_slot(self, slot):
        self._selection_model.selectionChanged.connect(slot)
コード例 #3
0
ファイル: itemmodels.py プロジェクト: tschoonj/OASYS1
 def select(self, index, flags=QItemSelectionModel.ClearAndSelect):
     if isinstance(index, int):
         index = self.model().index(index)
     return QItemSelectionModel.select(self, index, flags)
コード例 #4
0
class LayerStackModel(QAbstractListModel):
    canMoveSelectedUp = pyqtSignal("bool")
    canMoveSelectedDown = pyqtSignal("bool")
    canDeleteSelected = pyqtSignal("bool")

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

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

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

        self.layerRemoved.connect(_handleRemovedLayer)

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

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

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

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

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

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

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

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

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

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

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

        self.updateGUI()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def supportedDropActions(self):
        return Qt.MoveAction

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

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

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

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

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

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

    def _onSelectionChanged(self, selected, deselected):
        for idx in deselected.indexes():
            self[idx.row()].setActive(False)
        for idx in selected.indexes():
            self[idx.row()].setActive(True)
コード例 #5
0
ファイル: itemmodels.py プロジェクト: srio/Orange-XOPPY
 def select(self, index, flags=QItemSelectionModel.ClearAndSelect):
     if isinstance(index, int):
         index = self.model().index(index)
     return QItemSelectionModel.select(self, index, flags)
コード例 #6
0
class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, ):
        super(MainWindow, self).__init__()
        self.setupUi(self)
        self.fig_dict = {}

        self.tabWidget.setCurrentIndex(0)

        self.plotAreaVerticalLayout = QtWidgets.QVBoxLayout()
        self.plotsFrame.setLayout(self.plotAreaVerticalLayout)

        #initialize Table
        self.headerV = self.tableWidget.verticalHeader()
        self.headerV.show()

        # add a widget for previewing plots, they can then be added to the actual plot
        self.plotPreview = PlotWidget()
        self.plotAreaVerticalLayout.addWidget(self.plotPreview)

        # Create tree model to store sim data items and connect it to views
        self.simDataItemTreeModel = SimDataItemTreeModel()
        self.bdTableModel = BdTableModel()
        self.bdUserGeneratedTableModel = BdUserGeneratedCurvesTableModel()
        self.simDataItemTreeView.setModel(self.simDataItemTreeModel)
        self.plotPreview.tableView.setModel(self.bdTableModel)

        # connect a double clicked section of the bd table to a change of the anchor
        self.plotPreview.tableView.horizontalHeader(
        ).sectionDoubleClicked.connect(self.update_bd_table)
        self.plotPreview.tableView.verticalHeader(
        ).sectionDoubleClicked.connect(
            self.update_bd_user_generated_curves_table)

        # Set custom selection model, so that sub items are automatically
        # selected if parent is selected
        self._selection_model = QRecursiveSelectionModel(
            self.simDataItemTreeView.model())
        self.simDataItemTreeView.setSelectionModel(self._selection_model)

        # Connect list view with model for the selected values of tree view
        self.selectedSimulationDataItemListModel = OrderedDictModel()
        self.simDataItemListView.setModel(
            self.selectedSimulationDataItemListModel)
        self._selection_model.selectionChanged.connect(self.change_list)

        # set up signals and slots
        self.selectedSimulationDataItemListModel.items_changed.connect(
            self.update_variable_tree)

        # Connect signals of menus
        self.actionOpen_File.triggered.connect(
            self.simDataItemTreeView.add_file)
        self.actionOpen_Directory.triggered.connect(
            self.simDataItemTreeView.add_folder)
        self.actionOpen_Directory_List.triggered.connect(
            self.simDataItemTreeView.add_folder_list)
        self.actionHide_PlotSettings.triggered.connect(
            self.set_plot_settings_visibility)
        self.actionHide_Sequence.triggered.connect(
            self.set_sequence_widget_visibility)
        self.actionHide_Status.triggered.connect(
            self.set_status_widget_visibility)

        self.actionSave_Table.triggered.connect(self.save_bd_table)

        self.actionExport_Figure_as_Tikzpicture.triggered.connect(
            self.plotPreview.export_plot_tikz)

        self.actionExport_TableWidget.triggered.connect(
            self.export_table_to_csv)

        self.actionSave_Data.triggered.connect(self.save_current_selection)

        self.action_About.triggered.connect(self.open_about_page)

        self.variableTreeModel = VariableTreeModel()
        self.variableTreeView.setModel(self.variableTreeModel)
        self.plotsettings.visibilityChanged.connect(
            self.plot_settings_visibility_changed)
        self.sequenceWidget.visibilityChanged.connect(
            self.sequence_widget_visibility_changed)
        self.statusWidget.visibilityChanged.connect(
            self.status_widget_visibility_changed)

        # Set recursive selection model for variable view
        self._variable_tree_selection_model = QRecursiveSelectionModel(
            self.variableTreeView.model())
        self.variableTreeView.setSelectionModel(
            self._variable_tree_selection_model)

        # set up combo boxes for rate/psnr and interpolation options
        self.combo_interp.addItems(["pchip", "pol"])
        self.combo_rate_psnr.addItems(["drate", "dsnr"])
        self.combo_interp.currentIndexChanged.connect(self.on_combo_box)
        self.combo_rate_psnr.currentIndexChanged.connect(self.on_combo_box)

        # set up bd plot checkbox
        self.checkBox_bdplot.stateChanged.connect(self.update_bd_plot)

        self.curveWidget.hide()
        self.curveListModel = OrderedDictModel()
        self.curveListView.setModel(self.curveListModel)
        self.curveListSelectionModel = QItemSelectionModel(self.curveListModel)
        self.curveListView.setSelectionModel(self.curveListSelectionModel)
        self.curveListView.setSelectionMode(
            QtWidgets.QAbstractItemView.ExtendedSelection)
        self.curveWidget.visibilityChanged.connect(
            self.curve_widget_visibility_changed)

        self.actionGenerate_curve.triggered.connect(self.generate_new_curve)
        self.actionRemove_items.triggered.connect(self.remove)
        self.actionReload_files.triggered.connect(self.reload_files)

        self.settings = QSettings()
        self.get_recent_files()
        self.simDataItemTreeView.itemsOpened.connect(self.add_recent_files)

        self.watcher = QFileSystemWatcher(self)
        self.watcher.fileChanged.connect(self.warning_file_change)
        self.watcher.directoryChanged.connect(self.warning_file_change)
        self.simDataItemTreeView.parserThread.newParsedData.connect(
            self.add_files_to_watcher)
        self.show_file_changed_message = True
        self.reset_timer = QTimer(self)
        self.reset_timer.setSingleShot(True)
        self.reset_timer.setInterval(15000)
        self.reset_timer.timeout.connect(self._reset_file_changed_message)

        self.simDataItemTreeView.customContextMenuRequested.connect(
            self.show_sequences_context_menu)
        # self.curveListView.actionCalculateBD.triggered.connect(self.bd_user_generated_curves)

    # sets Visibility for the Plotsettings Widget
    def set_plot_settings_visibility(self):
        self.plotsettings.visibilityChanged.disconnect(
            self.plot_settings_visibility_changed)
        if self.plotsettings.isHidden():
            self.plotsettings.setVisible(True)
        else:
            self.plotsettings.setHidden(True)
        self.plotsettings.visibilityChanged.connect(
            self.plot_settings_visibility_changed)

    # updates the QAction if Visibility is changed
    def plot_settings_visibility_changed(self):
        if self.plotsettings.isHidden():
            self.actionHide_PlotSettings.setChecked(True)
        else:
            self.actionHide_PlotSettings.setChecked(False)
        self._variable_tree_selection_model.selectionChanged.connect(
            self.update_plot)
        self.curveListSelectionModel.selectionChanged.connect(self.update_plot)
        self.simDataItemTreeView.deleteKey.connect(self.remove)

    # sets Visibility for the Sequence Widget
    def set_sequence_widget_visibility(self):
        self.sequenceWidget.visibilityChanged.disconnect(
            self.sequence_widget_visibility_changed)
        if self.sequenceWidget.isHidden():
            self.sequenceWidget.setVisible(True)
        else:
            self.sequenceWidget.setHidden(True)
        self.sequenceWidget.visibilityChanged.connect(
            self.sequence_widget_visibility_changed)

    def sequence_widget_visibility_changed(self):
        if self.sequenceWidget.isHidden():
            self.actionHide_Sequence.setChecked(True)
        else:
            self.actionHide_Sequence.setChecked(False)

    # Sets Visibility for the Status Widget
    def set_status_widget_visibility(self):
        self.statusWidget.visibilityChanged.disconnect(
            self.status_widget_visibility_changed)
        if self.statusWidget.isHidden():
            self.statusWidget.setVisible(True)
        else:
            self.statusWidget.setHidden(True)
        self.statusWidget.visibilityChanged.connect(
            self.status_widget_visibility_changed)

    def status_widget_visibility_changed(self):
        if self.statusWidget.isHidden():
            self.actionHide_Status.setChecked(True)
        else:
            self.actionHide_Status.setChecked(False)

    def curve_widget_visibility_changed(self):
        if self.curveWidget.isHidden():
            self.curveListView.delete_key.disconnect()
        else:
            self.curveListView.delete_key.connect(self.remove_curves)

    def remove(self):
        values = self.selectedSimulationDataItemListModel.values()

        for value in values:
            self.watcher.removePath(value.path)
        # List call necessary to avoid runtime error because of elements changing
        # during iteration
        self._variable_tree_selection_model.selectionChanged.disconnect()
        # disconnect slot to avoid multiple function triggers by selectionChanged signal
        # not disconnecting slows program down significantly
        self._selection_model.selectionChanged.disconnect(self.change_list)
        self.simDataItemTreeModel.remove(list(values))
        self.change_list(QItemSelection(), QItemSelection())
        self._selection_model.selectionChanged.connect(self.change_list)
        self._variable_tree_selection_model.selectionChanged.connect(
            self.update_plot)
        if len(self.selectedSimulationDataItemListModel.values()) == 0:
            self.update_plot()

    def change_list(self, q_selected, q_deselected):
        """Extend superclass behavior by automatically adding the values of
           all selected items in :param: `q_selected` to value list model. """

        selected_q_indexes = q_deselected.indexes()

        q_reselect_indexes = []
        for q_index in self.simDataItemTreeView.selectedIndexes():
            if q_index not in selected_q_indexes:
                q_reselect_indexes.append(q_index)

        # Find all all values that are contained by selected tree items
        tuples = []
        for q_index in q_selected.indexes() + q_reselect_indexes:
            # Add values, ie. sim data items stored at the item, to the list
            # model.
            sim_data_items = q_index.internalPointer().values
            tuples.extend((e.path, e) for e in sim_data_items)

        # Overwrite all elements in dictionary by selected values
        # Note, that overwriting only issues one `updated` signal, and thus,
        # only rerenders the plots one time. Therefore, simply overwriting
        # is much more efficient, despite it would seem, that selectively
        # overwriting keys is.
        self.selectedSimulationDataItemListModel.clear_and_update_from_tuples(
            tuples)

    def get_selected_simulation_data_items(self):
        return [
            self.selectedSimulationDataItemListModel[key]
            for key in self.selectedSimulationDataItemListModel
        ]

    def get_plot_data_collection_from_selected_variables(self):
        """Get a :class: `dict` with y-variable as key and values as items
        from the current selection of the variable tree.

        :rtype: :class: `dict` of :class: `string` and :class: `list`
        """

        plot_data_collection = []
        for q_index in self.variableTreeView.selectedIndexes():
            item = q_index.internalPointer()
            if len(item.values) > 0:
                plot_data_collection.extend(item.values)

        return plot_data_collection

    def update_variable_tree(self):
        """Collect all SimDataItems currently selected, and create variable
        tree and corresponding data from it. Additionaly reselect all previously
        selected variables.
        """

        # Create a list of all currently selected paths
        # Note, that *q_index* can not be used directly, because it might
        # change if the variable tree is recreated
        selected_path_collection = []
        for q_index in self.variableTreeView.selectedIndexes():
            selected_path_collection.append(
                # Create list of identifiers from path
                # Note, that the root node is excluded from the path
                [
                    item.identifier
                    for item in q_index.internalPointer().path[1:]
                ])

        # Join the data of all currently selected items to a dictionary
        # tree
        sim_data_items = self.get_selected_simulation_data_items()
        dict_tree = dict_tree_from_sim_data_items(sim_data_items)
        # check if qp values are the same
        # self.check_qp(sim_data_items)

        # Reset variable tree and update it with *dict_tree*
        self.variableTreeModel.clear_and_update_from_dict_tree(dict_tree)

        # Auto expand variable tree
        self.variableTreeView.expandToDepth(1)

        # Reselect all variables, which also exist on the new tree
        for path in selected_path_collection:
            # Try to reselect, and do nothing, if path does not exist anymore
            try:
                # Reselect new item corresponding to the previously selected
                # path
                item = self.variableTreeModel.get_item_from_path(*path)
                self.variableTreeView.selectionModel().select(
                    self.variableTreeModel._get_index_from_item(item),
                    QItemSelectionModel.Select,
                )
            except KeyError:
                pass

    # TODO: it might be that some log files do not have a QP value, therefore the check_qp method must be
    #       implemented in a way that these files are not affected
    # def check_qp(self, sim_data_items): # check if qp values are the same for each sequence
    #     if len(sim_data_items) < 2: return
    #     sim_data_items.sort(key=lambda item: (item.sequence))
    #
    #     qp_list,list = [],[]
    #     seq = sim_data_items[0].sequence
    #     config = sim_data_items[0].config
    #
    #     for item in sim_data_items:
    #         if ((seq == item.sequence) & (config == item.config)):
    #             list.append(item.qp)
    #         elif (seq == item.sequence): #same sequence different config
    #             config = item.config
    #             qp_list.append(list)
    #             list = []
    #             list.append(item.qp)
    #         else:  # different sequence
    #             seq = item.sequence
    #             config = item.config
    #             qp_list.append(list)
    #             if not(all(list == qp_list[0] for list in qp_list)):
    #                 QtWidgets.QMessageBox.warning(self, "Warning",
    #                                               "Be careful! You chose a sequence with different QP.")
    #                 return
    #             list, qp_list = [], []
    #             list.append(item.qp)
    #     qp_list.append(list)
    #
    #     if not(all(list == qp_list[0] for list in qp_list )):
    #         QtWidgets.QMessageBox.warning(self, "Warning",
    #                                         "Be careful! You chose a sequence with different QP.")

    def check_labels(self):
        selectionmodel = self.variableTreeView.selectionModel()
        selected = self.variableTreeView.selectedIndexes()
        # return if no comparison needed
        if len(selected) < 2:
            return
        labelx = []
        labely = []
        for index in selected:
            x = index.internalPointer()
            if len(x.values) > 0:
                labelx.append(x.values[0].label[0])
                labely.append(x.values[0].label[1])

        if all(x == labelx[0] for x in labelx) and all(x == labely[0]
                                                       for x in labely):
            return

        else:
            QtWidgets.QMessageBox.information(
                self, "Error!",
                "You should not choose curves with different units.")
            selectionmodel.clearSelection()

    # updates the plot if the plot variable is changed
    def update_plot(self):
        # user-generated curves and curves loaded from files are not supposed to be mixed
        user_generated_curves = False
        if self.sender() == self._variable_tree_selection_model or self.sender(
        ) == self.curveListSelectionModel:
            self.check_labels()
            data_collection = self.get_plot_data_collection_from_selected_variables(
            )
            data_collection_user_generated = []
            for index in self.curveListView.selectedIndexes():
                data_collection_user_generated.append(
                    self.curveListModel[index.data()])
        else:
            return

        plot_data_collection = data_collection + data_collection_user_generated
        if len(data_collection_user_generated):
            self.plotPreview.tableView.setModel(self.bdUserGeneratedTableModel)
            self.plotPreview.change_plot(plot_data_collection, True)
        else:
            self.plotPreview.tableView.setModel(self.bdTableModel)
            self.update_table(data_collection)
            self.plotPreview.change_plot(plot_data_collection, False)
        if len(data_collection) and len(data_collection_user_generated):
            # don't mix user-generated and normal curves
            self.plotPreview.tableView.hide()
            self.plotPreview.label_warning.show()
            return

        self.plotPreview.tableView.show()
        self.plotPreview.label_warning.hide()
        self.plotPreview.tableView.model().update(
            plot_data_collection, self.combo_rate_psnr.currentText(),
            self.combo_interp.currentText(),
            not (self.checkBox_bdplot.isChecked()))

    def get_table_header(self, plot_data_collection):
        tmp_legend = []
        tmp_config = []

        # make legend
        for plot_data in plot_data_collection:
            tmp = []
            for identifiers in plot_data.identifiers[1:]:
                tmp += identifiers.split(sep)
            tmp2 = tmp + plot_data.path
            tmp_legend.append(tmp2)
            tmp_config.append(tmp)

        legend = []
        config = []
        for c in tmp_legend:
            result = list(
                filter(lambda x: all(x in l for l in tmp_legend) == False, c))
            if result == []: result = [plot_data.path[-1]]
            legend.append(" ".join(result))
        if len(tmp_legend) == 1:
            legend = [plot_data.path[-1]]

        #make config
        for c in tmp_config:
            result = list(
                filter(lambda x: all(x in l for l in tmp_config) == False, c))
            if ((set([" ".join(result)]) - set(config) != set()) &
                (result != [])):
                config.append(" ".join(result))

        result = (legend, config)
        return result

    #updates the table
    def update_table(self, plot_data_collection):

        self.tableWidget.clear()
        self.tableWidget.setColumnCount(0)
        self.tableWidget.setRowCount(0)

        if plot_data_collection != []:
            if 'Temporal' in plot_data_collection[0].path:
                self.change_table_temporal(plot_data_collection)
            else:
                self.change_table_summary(plot_data_collection)

        self.tableWidget.resizeColumnsToContents()

    def change_table_temporal(self, plot_data_collect):

        plot_data_collection = plot_data_collect
        self.tableWidget.setRowCount(len(plot_data_collection))
        plot_count = data_count = 0
        data_names = []
        plot_data_collection.sort(
            key=lambda plot_data: (plot_data.identifiers))
        legend = self.get_table_header(plot_data_collection)
        header = legend[0]

        for plot_data in plot_data_collection:
            values = ((float(x), float(y)) for (x, y) in plot_data.values)

            sorted_value_pairs = sorted(values, key=lambda pair: pair[0])
            [xs, ys] = list(zip(*sorted_value_pairs))

            # make header
            if plot_data.identifiers[0] not in data_names:
                self.tableWidget.insertRow(plot_count)
                v_item = QtWidgets.QTableWidgetItem(
                    str(plot_data.identifiers[0]))
                font = self.tableWidget.font()
                v_item.setData(
                    6, QtGui.QFont(self.tableWidget.font().setBold(True)))
                v_item.setData(6, QtGui.QFont("Ubuntu", 11, QtGui.QFont.Bold))
                self.tableWidget.setVerticalHeaderItem(plot_count, v_item)
                header_count = plot_count
                data_names.append(plot_data.identifiers[0])
                plot_count += 1

            # round data
            if plot_data.label[1] == 'dB':
                ys = tuple(map(lambda i: round(i, 1), ys))

            self.tableWidget.horizontalHeader().setVisible(False)
            # fill up column per column
            for column_count in range(0, len(xs)):

                self.tableWidget.setCurrentCell(plot_count, column_count)
                if column_count > self.tableWidget.currentColumn():
                    self.tableWidget.insertColumn(column_count)
                self.tableWidget.setItem(
                    plot_count, column_count,
                    QtWidgets.QTableWidgetItem(str(ys[column_count])))
                self.tableWidget.setVerticalHeaderItem(
                    plot_count,
                    QtWidgets.QTableWidgetItem(
                        str(header[data_count]) + " [" +
                        str(plot_data.label[1]) + "] "))
                new_item = QtWidgets.QTableWidgetItem(str(xs[column_count]))
                new_item.setData(6, QtGui.QFont("Ubuntu", 11,
                                                QtGui.QFont.Bold))
                self.tableWidget.setItem(header_count, column_count, new_item)

                column_count += 1

            plot_count += 1
            data_count += 1

    def change_table_summary(self, plot_data_collect):

        plot_data_collection = plot_data_collect
        header_count = plot_count = data_count = config_count = column_saver = 0
        data_names = []
        plot_data_collection.sort(key=lambda plot_data: plot_data.path[-1])
        plot_data_collection.sort(
            key=lambda plot_data: plot_data.identifiers[0])
        legend = self.get_table_header(plot_data_collection)
        header = legend[0]
        config = legend[1]

        if ((config == []) | (len(config) == 1)):
            self.change_table_temporal(plot_data_collection)
            return

        self.tableWidget.setRowCount(len(plot_data_collection) / len(config))

        for plot_data in plot_data_collection:

            values = ((float(x), float(y)) for (x, y) in plot_data.values)

            sorted_value_pairs = sorted(values, key=lambda pair: pair[0])
            [xs, ys] = list(zip(*sorted_value_pairs))

            # make header, important if more than one plot
            if plot_data.identifiers[0] not in data_names:
                self.tableWidget.insertRow(plot_count)
                v_item = QtWidgets.QTableWidgetItem(
                    str(plot_data.identifiers[0]))
                v_item.setData(6, QtGui.QFont("Ubuntu", 11, QtGui.QFont.Bold))
                self.tableWidget.setVerticalHeaderItem(plot_count, v_item)
                header_count = plot_count
                data_names.append(plot_data.identifiers[0])
                plot_count += 1

            # round data
            if plot_data.label[1] == 'dB':
                ys = tuple(map(lambda i: round(i, 1), ys))

            #horizontal header if more than one config
            if len(config) > 1:
                self.tableWidget.horizontalHeader().setVisible(True)
            else:
                self.tableWidget.horizontalHeader().setVisible(False)

            for column_count in range(0, len(xs)):

                columns = column_saver + column_count
                if (((column_saver + column_count) >=
                     self.tableWidget.columnCount()) |
                    (self.tableWidget.columnCount() == 0)):
                    self.tableWidget.insertColumn(column_saver + column_count)
                if plot_count >= self.tableWidget.rowCount():
                    self.tableWidget.insertRow(plot_count)
                # units in first row of table
                new_item = QtWidgets.QTableWidgetItem(plot_data.label[0] +
                                                      ' | ' +
                                                      plot_data.label[1])
                new_item.setData(6, QtGui.QFont("Ubuntu", 11,
                                                QtGui.QFont.Bold))
                self.tableWidget.setItem(header_count,
                                         column_saver + column_count, new_item)
                #self.tableWidget.setItem(header_count, column_saver + column_count, QtWidgets.QTableWidgetItem(plot_data.label[0] + ' | ' + plot_data.label[1]))
                # x and y-value in one cell
                self.tableWidget.setItem(
                    plot_count, columns,
                    QtWidgets.QTableWidgetItem(
                        str(xs[column_count]) + ' | ' + str(ys[column_count])))
                # header
                self.tableWidget.setHorizontalHeaderItem(
                    column_saver + column_count,
                    QtWidgets.QTableWidgetItem(str(config[config_count])))
                column_count += 1

            if config[config_count] == header[data_count]:
                header[data_count] = header[data_count].replace(
                    config[config_count], plot_data.path[-1])
            elif config[config_count] in header[data_count]:
                header[data_count] = header[data_count].replace(
                    config[config_count], '')

            self.tableWidget.setVerticalHeaderItem(
                plot_count,
                QtWidgets.QTableWidgetItem(str(header[data_count])))
            column_saver = column_saver + column_count
            config_count += 1

            if config_count == len(config):
                plot_count += 1
                column_saver = config_count = 0
            data_count += 1

    def update_bd_table(self, index):
        # update bd table, the index determines the anchor,
        # if it is non integer per default the first config is regarded as
        # anchor

        self.bdTableModel.update_table(self.combo_rate_psnr.currentText(),
                                       self.combo_interp.currentText(), index,
                                       not (self.checkBox_bdplot.isChecked()))

    def update_bd_user_generated_curves_table(self, index):
        clicked_text = self.bdUserGeneratedTableModel.headerData(
            index, Qt.Vertical, Qt.DisplayRole)
        self.bdUserGeneratedTableModel.update(
            None, self.combo_rate_psnr.currentText(),
            self.combo_interp.currentText(),
            not (self.checkBox_bdplot.isChecked()), clicked_text)

    def update_bd_plot(self):
        data_collection = self.get_plot_data_collection_from_selected_variables(
        )
        data_collection_user_generated = []
        for index in self.curveListSelectionModel.selectedIndexes():
            data_collection_user_generated.append(
                self.curveListModel[index.data()])

        if len(data_collection):
            self.bdTableModel.update(data_collection,
                                     self.combo_rate_psnr.currentText(),
                                     self.combo_interp.currentText(),
                                     not (self.checkBox_bdplot.isChecked()))
        elif len(data_collection_user_generated):
            self.bdTableModel.update(data_collection_user_generated,
                                     self.combo_rate_psnr.currentText(),
                                     self.combo_interp.currentText(),
                                     not (self.checkBox_bdplot.isChecked()))

    def export_table_to_csv(self):
        # remember that the decimal mark is '.'
        if self.tableWidget.rowCount() > 0:
            path, extension = QtWidgets.QFileDialog.getSaveFileName(
                self, 'Save Table View as', '.', 'CSV (*.csv)')
            if path != '':
                if '.csv' not in path:
                    path += '.csv'
                with open(str(path), 'w', newline='') as stream:
                    writer = csv.writer(stream)
                    for row in range(self.tableWidget.rowCount()):
                        rowdata = []
                        rowdata.append(
                            str(
                                self.tableWidget.verticalHeaderItem(row).data(
                                    0)))  #data(0) = data(Qt.displayRole)
                        for column in range(self.tableWidget.columnCount()):
                            item = self.tableWidget.item(row, column)
                            if item is not None:
                                rowdata.append(str(item.text()))
                            else:
                                rowdata.append('')
                        writer.writerow(rowdata)

    def save_bd_table(self):
        if self.bdTableModel.rowCount(self) == 0:
            return
        filename, extension = QtWidgets.QFileDialog.getSaveFileName(
            self, 'Save Table as', '.', 'Latex (*.tex)')
        if filename != '':
            if '.tex' not in filename:
                filename += '.tex'
            self.bdTableModel.export_to_latex(filename)

    def on_combo_box(self):
        # just update the bd table but do not change the anchor
        self.update_bd_table(-1)

    def save_current_selection(self):
        """Saves the current selected sim data item collection"""
        if not self.get_selected_simulation_data_items():
            msg = QtWidgets.QMessageBox(self)  # use self as parent here
            msg.setIcon(QtWidgets.QMessageBox.Information)
            msg.setText(
                "You did not select any simulation data item to store\n"
                "Please make a selection and try again.")
            msg.setWindowTitle("Info")
            msg.show()
            return
        filename, extension = QtWidgets.QFileDialog.getSaveFileName(
            self, 'Save RD data as', '.', 'RDPlot (*.rd)')
        if filename != '':
            if '.rd' not in filename:
                filename += '.rd'
            f = open(filename, 'w')
            f.write(
                jsonpickle.encode(self.get_selected_simulation_data_items()))
            f.close()

    def process_cmd_line_args(self, args):
        """Processes cmd line arguments. Those are only pathes or files."""
        for path in args[1:]:
            if not isdir(path) and not isfile(path):
                continue
            if path.endswith('.rd'):
                f = open(path, 'r')
                json_str = f.read()
                sim_data_items = jsonpickle.decode(json_str)
                self.simDataItemTreeModel.update(sim_data_items, False)
                f.close()
                continue

            self.simDataItemTreeView.msg.show()
            self.simDataItemTreeView.parserThread.add_path(path)
            self.simDataItemTreeView.parserThread.start()

    def open_about_page(self):
        """Opens and displays an Html About file"""
        try:
            html_path = path.abspath(here + '/docs/about.html')
            html_file = open(html_path, 'r', encoding='utf-8', errors='ignore')
            source_code = html_file.read()

            try:
                f = open(here + '/version.txt', 'r')
                app_version = f.readline()
            except:
                app_version = 'could not detect version'

            source_code = source_code.replace("##VERSION##", app_version)
            source_code = source_code.replace("##here##", here)
            about_dialog = QtWidgets.QDialog(self)
            about_dialog.setWindowTitle("About RDPlot")
            about_dialog.setMaximumSize(950, 800)
            about_text = QtWidgets.QTextBrowser(about_dialog)
            about_text.setMinimumWidth(950)
            about_text.setMinimumHeight(800)
            about_text.setHtml(source_code)
            about_text.setOpenExternalLinks(True)
            about_text.show()
            about_dialog.exec_()
            about_dialog.close()
            about_text.close()
        except IOError:
            html_error = QtWidgets.QMessageBox()
            html_error.setIcon(QtWidgets.QMessageBox.Critical)
            html_error.setText("Error opening about or help")
            html_error.setInformativeText(
                "The html file from the resource could not be loaded.")
            html_error.exec_()

    def generate_new_curve(self):
        plot_data_collection = self.get_plot_data_collection_from_selected_variables(
        )
        if plot_data_collection:
            new_plot_values = []
            for _plot_data in plot_data_collection:
                new_plot_values.extend(_plot_data.values)
            if len(new_plot_values) < 4:
                QtWidgets.QMessageBox.warning(
                    self, "Warning!", "You didn't select at least 4 points.")
            else:
                curve_name, ok = QtWidgets.QInputDialog.getText(
                    self, "New curve",
                    "Please enter a name for the new curve.\n"
                    "If you enter an already existing name,\nits data will be overwritten."
                )
                curve_name = curve_name.strip()
                if curve_name is not '':
                    new_plot_data = PlotData([curve_name], new_plot_values, [],
                                             plot_data_collection[0].label)
                    self.add_curve(curve_name, new_plot_data)
                else:
                    QtWidgets.QMessageBox.warning(
                        self, "Warning!", "Please enter a valid name.")
        else:
            QtWidgets.QMessageBox.warning(
                self, "Warning!", "You didn't select at least 4 points.")

    def add_curve(self, name, data):
        if self.curveWidget.isHidden():
            self.curveWidget.show()
        data_tuple = (name, data)
        self.curveListModel.update_from_tuples((data_tuple, ))
        # all_indexes = QItemSelection(self.curveListModel.index(0),
        #                             self.curveListModel.index(self.curveListModel.rowCount(QModelIndex())))
        # self.curveListSelectionModel.select(all_indexes, QItemSelectionModel.Clear)
        # self.curveListSelectionModel.select(self.curveListModel.index(self.curveListModel.rowCount(QModelIndex())-1),
        #                                    QItemSelectionModel.Select)
        # self.curveListView.setFocus()

    def remove_curves(self):
        # todo integrate bjontegaard for generated curves(should be fully functional already)
        curves_to_remove = []
        for index in self.curveListSelectionModel.selectedIndexes():
            curves_to_remove.append(index.data())
        self.curveListModel.remove_keys(curves_to_remove)
        if len(self.curveListModel) > 0:
            self.curveListSelectionModel.select(self.curveListModel.index(0),
                                                QItemSelectionModel.Select)
        else:
            self.curveWidget.hide()
            self.update_plot()

    def get_recent_files(self):
        recent_files = self.settings.value('recentFiles')
        if recent_files is not None:
            for recent_file in recent_files:
                if path.exists(recent_file):
                    action = self.menuRecent_files.addAction(recent_file)
                    action.triggered.connect(self.open_recent_file)

    def open_recent_file(self):
        path_recent = self.sender().text()
        if path.isdir(path_recent):
            self.simDataItemTreeView.add_folder(path_recent)
        else:
            self.simDataItemTreeView.add_file(path_recent)

    def add_recent_files(self, files, reload):
        # files doesn't necessarily have to just be a list of files
        # it can also be a directory
        if not reload:
            recent_files = self.settings.value('recentFiles')
            if recent_files is None:
                recent_files = []
            for file in files:

                if file in recent_files:
                    # put our file on top of the list
                    recent_files.remove(file)
                recent_files.insert(0, file)
            while len(recent_files) > 5:
                del recent_files[-1]
            self.settings.setValue('recentFiles', recent_files)

            self.menuRecent_files.clear()
            for recent_file in recent_files:
                if path.exists(recent_file):
                    action = self.menuRecent_files.addAction(recent_file)
                    action.triggered.connect(self.open_recent_file)

    def add_files_to_watcher(self, items):
        for item in items:
            if isfile(item.path):
                self.watcher.addPath(item.path)

    def warning_file_change(self, path_item):
        # inform user about the fact that one of the loaded files has been changed since the application has started
        # timer is used to avoid spamming the user when multiple files are deleted in a row
        # retrieve affected notes and parent nodes
        # change their style in the tree view to indicate which files are affected
        if self.show_file_changed_message:
            self.show_file_changed_message = False
            self.reset_timer.start()
            QtWidgets.QMessageBox.warning(
                self, 'File change',
                'One or more of your loaded files have been changed.\n'
                'You can choose to reload them.\n'
                'Hint: Changed files are greyed out in the sequences widget.')
        else:
            self.reset_timer.stop()
            self.reset_timer.start()

        affected_notes = []
        for leaf in self.simDataItemTreeModel.root.leafs:
            for value in leaf.values:
                if value.path == path_item:
                    affected_notes.append(leaf)
        for node in affected_notes:
            node_index = self.simDataItemTreeModel._get_index_from_item(node)
            node.setProperty('needs_reload', 'True')
            parent = self.simDataItemTreeModel.parent(node_index)
            level = 0
            while parent.isValid() and level < 2:  #MAX_LEVEL
                parent.internalPointer().setProperty('needs_reload', 'True')
                parent = self.simDataItemTreeModel.parent(parent)
                level += 1

    def _reset_file_changed_message(self):
        self.show_file_changed_message = True

    def reload_files(self):
        # remove all selected files first
        # reload available files
        # could possibly limit this to only files that we know have been changed
        def check_children(parent):
            if len(parent.children) > 0:
                for child in parent.children:
                    if not check_children(child):
                        return False
                parent.setProperty('needs_reload', 'False')
                return True
            else:
                if parent.property('needs_reload') == 'True':
                    return False
                return True

        values = self.selectedSimulationDataItemListModel.values()
        if len(values) == 0:
            for index in self.simDataItemTreeModel.root.leafs:
                for sim_data_item in index.values:
                    values.append(sim_data_item)
        items_to_be_reloaded = []
        for value in values:
            if path.exists(value.path):
                # reload file
                items_to_be_reloaded.append(value)

        self._variable_tree_selection_model.selectionChanged.disconnect()
        self._selection_model.selectionChanged.disconnect(self.change_list)
        self.simDataItemTreeModel.remove(values)

        self.simDataItemTreeView.msg.show()
        for item in items_to_be_reloaded:
            self.simDataItemTreeView.add_file(item.path, reload=True)

        self.change_list(QItemSelection(), QItemSelection())
        self._selection_model.selectionChanged.connect(self.change_list)
        self._variable_tree_selection_model.selectionChanged.connect(
            self.update_plot)

        for node in self.simDataItemTreeModel.root.children:
            # remove grey font color if all changed files have been reloaded
            # have to check every single item because possible deletion of older nodes makes things very difficult
            check_children(node)

    def show_sequences_context_menu(self, position):
        self.menuEdit.exec(self.simDataItemTreeView.mapToGlobal(position))
コード例 #7
0
class SettingsDialog(QDialog):
    """In order to use a class as an option add it to self.widgets"""

    def __init__(self, controls, parent=None, status=None):
        QDialog.__init__(self, parent)
        self.setWindowTitle("puddletag settings")
        winsettings('settingswin', self)

        built_in = [
            (translate("Settings", 'General'),
             GeneralSettings(controls), controls),
            (translate("Settings", 'Confirmations'),
             confirmations.Settings(), None),
            (translate("Settings", 'Mappings'), TagMappings(), None),
            (translate("Settings", 'Playlist'), Playlist(), None),
            (translate("Settings", 'Colours'), ColorEdit(), status['table']),
            (translate("Settings", 'Genres'),
             genres.Genres(status=status), None),
            (translate("Settings", 'Tags'), Tags(status=status),
             status['table']),
            (translate("Settings", 'Plugins'), PluginConfig(), None),
            (translate("Settings", 'Shortcuts'),
             ActionEditorDialog(status['actions']), None), ]

        d = dict(enumerate(built_in))

        i = len(d)
        for control in controls:
            if hasattr(control, SETTINGSWIN):
                c = getattr(control, SETTINGSWIN)(status=status)
                d[i] = [c.title, c, control]
                i += 1
        for c in config_widgets:
            d[i] = [c.title, c(status=status), None]
            i += 1

        self._widgets = d

        self.listbox = SettingsList()
        self.model = ListModel(d)
        self.listbox.setModel(self.model)

        self.stack = QStackedWidget()
        self.stack.setFrameStyle(QFrame.StyledPanel)

        self.grid = QGridLayout()
        self.grid.addWidget(self.listbox)
        self.grid.addWidget(self.stack, 0, 1)
        self.grid.setColumnStretch(1, 2)
        self.setLayout(self.grid)

        self.listbox.selectionChangedSignal.connect(self.showOption)

        selection = QItemSelection()
        self.selectionModel = QItemSelectionModel(self.model)
        index = self.model.index(0, 0)
        selection.select(index, index)
        self.listbox.setSelectionModel(self.selectionModel)
        self.selectionModel.select(selection, QItemSelectionModel.Select)

        self.okbuttons = OKCancel()
        self.okbuttons.okButton.setDefault(True)
        self.grid.addLayout(self.okbuttons, 1, 0, 1, 2)

        self.okbuttons.ok.connect(self.saveSettings)
        self.accepted.connect(self.saveSettings)
        self.okbuttons.cancel.connect(self.close)

    def showOption(self, option):
        widget = self._widgets[option][1]
        stack = self.stack
        if stack.indexOf(widget) == -1:
            stack.addWidget(widget)
        stack.setCurrentWidget(widget)
        if self.width() < self.sizeHint().width():
            self.setMinimumWidth(self.sizeHint().width())

    def saveSettings(self):
        for z in self._widgets.values():
            try:
                z[1].applySettings(z[2])
            except SettingsError as e:
                QMessageBox.warning(self, 'puddletag',
                                    translate('Settings', 'An error occurred while saving the settings of <b>%1</b>: %2').arg(z[0]).arg(str(e)))
                return
        self.close()
コード例 #8
0
class GraphDigitGraphicsView(QGraphicsView):
    sigMouseMovePoint = pyqtSignal(QPoint, QPointF)
    sigModified = pyqtSignal(bool)
    sigNewCurveAdded = pyqtSignal()

    # 自定义信号sigMouseMovePoint,当鼠标移动时,在mouseMoveEvent事件中,将当前的鼠标位置发送出去
    # QPoint--传递的是view坐标
    def __init__(self, parent=None):
        super(GraphDigitGraphicsView, self).__init__(parent)
        # scene
        rect = QRectF(0, 0, 300, 400)
        self.scene = QGraphicsScene(rect)  # 创建场景 参数:场景区域
        self.setScene(self.scene)  # 给视图窗口设置场景
        # image
        self.graphicsPixmapItem = QGraphicsPixmapItem()  # chart image
        self.scene.addItem(self.graphicsPixmapItem)
        # setAntialias
        self.setRenderHint(QPainter.Antialiasing)
        # image object stored in project data
        # item objects storage
        self.axesxObjs = {}
        self.axesyObjs = {}
        self.gridObjs = []
        self.curveObjs = {}
        self.pointObjs = {}
        # axis coord stored in project data
        # grid setting stored in project data
        # grid position
        self.gridxpos = []
        self.gridypos = []
        # axes curve and point datamodel
        self.axesxModel = QStandardItemModel()
        self.axesyModel = QStandardItemModel()
        self.axesxSelectModel = QItemSelectionModel(self.axesxModel)
        self.axesySelectModel = QItemSelectionModel(self.axesyModel)
        self.curveModel = QStandardItemModel()
        self.pointsModel = QStandardItemModel()

        self.curveModel.setHorizontalHeaderLabels(
            ["current", "name", "visible"])
        self.pointsModel.setHorizontalHeaderLabels(["order", "x", "y"])
        self.axesxModel.setHorizontalHeaderLabels(["position", "x"])
        self.axesyModel.setHorizontalHeaderLabels(["position", "y"])

        ###
        self.xNo = 0
        self.yNo = 0

        ###
        self.mode = OpMode.default
        # data manage
        self.proj = Digi()

        # init
        self.currentCurve = None
        self.pointsModel.itemChanged.connect(self.changePointOrder)
        self.curveModel.itemChanged.connect(self.changeCurveVisible)
        self.scene.selectionChanged.connect(self.slotSceneSeletChanged)

        # state
        self._lastCurve = None

    def load(self, proj):
        # image # only for load
        self.scaleGraphImage(proj.imgScale)
        # axes
        xadded = []
        yadded = []
        for xpos, xcoord in proj.data["axesxObjs"].items():
            if xpos in xadded:
                continue
            else:
                xadded.append(xpos)
            item = QGraphicsAxesItem(
                0,
                self.scene.sceneRect().y(), 0,
                self.scene.sceneRect().y() + self.scene.sceneRect().height())
            item.setFlags(QGraphicsItem.ItemIsSelectable
                          | QGraphicsItem.ItemIsFocusable
                          | QGraphicsItem.ItemIsMovable)
            item.setPos(xpos, 0)
            item.axis = "x"
            item.setPen(QPen(Qt.red, 1, Qt.DashLine))
            self.scene.addItem(item)
            self.axesxObjs[item] = xcoord
            labelitem = QStandardItem("x:{}".format(xpos))
            labelitem.setData(item)
            self.axesxModel.appendRow([labelitem, QStandardItem(str(xcoord))])
            self.xNo += 1
            self.axesxModel.setVerticalHeaderItem(
                self.axesxModel.rowCount() - 1,
                QStandardItem("x{}".format(self.xNo)))
        for ypos, ycoord in proj.data["axesyObjs"].items():
            if ypos in yadded:
                continue
            else:
                yadded.append(ypos)
            item = QGraphicsAxesItem(
                self.scene.sceneRect().x(), 0,
                self.scene.sceneRect().x() + self.scene.sceneRect().width(), 0)
            item.setFlags(QGraphicsItem.ItemIsSelectable
                          | QGraphicsItem.ItemIsFocusable
                          | QGraphicsItem.ItemIsMovable)
            item.setPos(0, ypos)
            item.axis = "y"
            item.setPen(QPen(Qt.red, 1, Qt.DashLine))
            self.scene.addItem(item)
            self.axesyObjs[item] = ycoord
            labelitem = QStandardItem("y:{}".format(ypos))
            labelitem.setData(item)
            self.axesyModel.appendRow([labelitem, QStandardItem(str(ycoord))])
        # curve
        for curve in proj.data["curves"]:
            self.pointObjs[curve] = []
            self.addCurve(curve)
            clr = RandItem.nextColor()
            for x, y in proj.data["curves"][curve]:
                ptitem = QGraphicsPointItem()
                ptitem.pointColor = clr
                ptitem.linewidth = 1
                ptitem.setPos(x, y)
                ptitem.parentCurve = curve
                ptitem.setFlags(QGraphicsItem.ItemIsSelectable
                                | QGraphicsItem.ItemIsFocusable
                                | QGraphicsItem.ItemIsMovable)
                i = pointInsertPosition(ptitem,
                                        self.pointObjs[self.currentCurve])
                self.pointObjs[self.currentCurve].insert(i, ptitem)
                self.scene.addItem(ptitem)
            self.updateCurve(curve)
        # grid
        self.calGridCoord()
        self.updateGrid()
        self.sigModified.emit(True)
        self.mode = OpMode.select

    def resetview(self):
        self.setGraphImage(None)
        self.scaleGraphImage(1)
        for obj in self.axesxObjs:
            self.scene.removeItem(obj)
        self.axesxObjs = {}
        for obj in self.axesyObjs:
            self.scene.removeItem(obj)
        self.axesyObjs = {}
        for obj in self.gridObjs:
            self.scene.removeItem(obj)
        self.gridObjs = []
        for curve in self.curveObjs:
            for obj in self.curveObjs[curve]:
                self.scene.removeItem(obj)
        self.curveObjs = {}
        for curve in self.pointObjs:
            for obj in self.pointObjs[curve]:
                self.scene.removeItem(obj)
        self.pointObjs = {}
        self.axesxModel.clear()
        self.axesyModel.clear()
        self.curveModel.clear()
        self.pointsModel.clear()
        self.axesxSelectModel.clear()
        self.axesySelectModel.clear()
        self.curveModel.setHorizontalHeaderLabels(
            ["current", "name", "visible"])
        self.pointsModel.setHorizontalHeaderLabels(["order", "x", "y"])
        self.axesxModel.setHorizontalHeaderLabels(["position", "x"])
        self.axesyModel.setHorizontalHeaderLabels(["position", "y"])

        self.xNo = 0
        self.yNo = 0

        ###
        self.mode = OpMode.default
        # data manage
        self.proj.resetData(True)
        # init
        self.currentCurve = None
        self._lastCurve = None

    def dump(self):
        proj = self.proj
        proj.data["axesxObjs"] = {}  # [x1,x2,x3]
        proj.data["axesyObjs"] = {}  # [y1,y2,y3]
        proj.data["curves"] = {}  # {'default':(x1,y1),(x2,y2)}
        # axes
        for item, xcoord in self.axesxObjs.items():
            proj.data["axesxObjs"][item.pos().x()] = xcoord
        for item, ycoord in self.axesyObjs.items():
            proj.data["axesyObjs"][item.pos().y()] = ycoord
        # curve
        for curve in self.pointObjs:
            proj.data["curves"][curve] = []
            for item in self.pointObjs[curve]:
                proj.data["curves"][curve].append((item.x(), item.y()))

    def mouseMoveEvent(self, evt):
        pt = evt.pos()  # 获取鼠标坐标--view坐标
        self.sigMouseMovePoint.emit(pt, self.mapToScene(pt))  # 发送鼠标位置
        QGraphicsView.mouseMoveEvent(self, evt)
        item = self.scene.focusItem()
        if not item:
            items = self.scene.selectedItems()
            if len(items) != 1:
                return
            else:
                item = items[0]
        if item:
            if isinstance(item, QGraphicsPointItem) and item.parentCurve:
                # self.changeCurrentCurve(item.parentCurve)
                self.updateCurve(self.currentCurve, Qt.red)
                self.updateCurvePoints(self.currentCurve)
                self.sigModified.emit(True)
            elif isinstance(item, QGraphicsAxesItem):
                if item.axis == "x":
                    if self.mode != OpMode.axesx:
                        self.scene.clearSelection()
                        return
                    self.axesxSelectModel.clearSelection()
                    self.axesySelectModel.clearSelection()
                    for i in range(self.axesxModel.rowCount()):
                        if self.axesxModel.item(i, 0).data() is item:
                            parent = QModelIndex()
                            topleftindex = self.axesxModel.index(
                                i, 0,
                                parent)  # self.axesxModel.item(i,0).index()
                            rightbottomindex = self.axesxModel.index(
                                i, 1, parent)
                            self.axesxSelectModel.select(
                                QItemSelection(topleftindex, rightbottomindex),
                                QItemSelectionModel.Select)
                            self.axesxModel.item(i, 0).setText("x:{}".format(
                                evt.pos().x()))
                            break

                    item.setLine(
                        0,
                        self.scene.sceneRect().y(), 0,
                        self.scene.sceneRect().y() +
                        self.scene.sceneRect().height())
                    item.setPos(self.mapToScene(evt.pos()).x(), 0)
                    self.sigModified.emit(True)
                    self.calGridCoord()
                    self.updateGrid()

                elif item.axis == "y":
                    if self.mode != OpMode.axesy:
                        self.scene.clearSelection()
                        return
                    self.axesxSelectModel.clearSelection()
                    self.axesySelectModel.clearSelection()
                    for i in range(self.axesyModel.rowCount()):
                        if self.axesyModel.item(i, 0).data() is item:
                            topleftindex = self.axesyModel.index(
                                i, 0)  # self.axesxModel.item(i,0).index()
                            rightbottomindex = self.axesyModel.index(i, 1)
                            self.axesySelectModel.select(
                                QItemSelection(topleftindex, rightbottomindex),
                                QItemSelectionModel.Select)
                            self.axesyModel.item(i, 0).setText("y:{}".format(
                                evt.pos().y()))
                            break

                    item.setLine(
                        self.scene.sceneRect().x(), 0,
                        self.scene.sceneRect().x() +
                        self.scene.sceneRect().width(), 0)
                    item.setPos(0, self.mapToScene(evt.pos()).y())
                    self.sigModified.emit(True)
                    self.calGridCoord()
                    self.updateGrid()

        # self.updateCurve(self.currentCurve)
        # self.repaint()
        # self.setDragMode(QGraphicsView.NoDrag) #(RubberBandDrag) #ScrollHandDrag) #NoDrag)

    def mousePressEvent(self, event):
        super().mousePressEvent(event)
        self.__pressPt = event.pos()
        ptscene = self.mapToScene(event.pos())
        if self.mode is OpMode.axesx:
            for axisitem in self.axesxObjs:
                if abs(ptscene.x() - axisitem.pos().x()) < 5:
                    self.scene.clearSelection()
                    axisitem.setSelected(True)
                    return
        elif self.mode is OpMode.axesy:
            for axisitem in self.axesyObjs:
                if abs(ptscene.y() - axisitem.pos().y()) < 5:
                    self.scene.clearSelection()
                    axisitem.setSelected(True)
                    return

    def keyPressEvent(self, event: QKeyEvent) -> None:
        # super().keyPressEvent(event)
        item = self.scene.focusItem()
        if not item:
            items = self.scene.selectedItems()
            if len(items) != 1:
                return
            else:
                item = items[0]
        if item:
            if isinstance(item, QGraphicsPointItem) and item.parentCurve:
                if event.key() == Qt.Key_Up:
                    item.setPos(item.pos().x(), item.pos().y() - 1)
                elif event.key() == Qt.Key_Down:
                    item.setPos(item.pos().x(), item.pos().y() + 1)
                elif event.key() == Qt.Key_Left:
                    item.setPos(item.pos().x() - 1, item.pos().y())
                elif event.key() == Qt.Key_Right:
                    item.setPos(item.pos().x() + 1, item.pos().y())
                else:
                    return
                self.updateCurve(self.currentCurve, Qt.red)
                self.updateCurvePoints(self.currentCurve)
                self.sigModified.emit(True)
            elif isinstance(item, QGraphicsAxesItem):
                if item.axis == "x":
                    if event.key() == Qt.Key_Left:
                        item.moveBy(-1, 0)
                    elif event.key() == Qt.Key_Right:
                        item.moveBy(1, 0)
                    else:
                        return
                    self.sigModified.emit(True)
                    self.calGridCoord()
                    self.updateGrid()

                    self.axesxSelectModel.clearSelection()
                    self.axesySelectModel.clearSelection()
                    for i in range(self.axesxModel.rowCount()):
                        if self.axesxModel.item(i, 0).data() is item:
                            parent = QModelIndex()
                            topleftindex = self.axesxModel.index(
                                i, 0,
                                parent)  # self.axesxModel.item(i,0).index()
                            rightbottomindex = self.axesxModel.index(
                                i, 1, parent)
                            self.axesxSelectModel.select(
                                QItemSelection(topleftindex, rightbottomindex),
                                QItemSelectionModel.Select)
                            self.axesxModel.item(i, 0).setText("x:{}".format(
                                item.pos().x()))
                            break

                elif item.axis == "y":
                    if event.key() == Qt.Key_Up:
                        item.setPos(0, item.pos().y() - 1)
                    elif event.key() == Qt.Key_Down:
                        item.setPos(0, item.pos().y() + 1)
                    else:
                        return
                    self.sigModified.emit(True)
                    self.calGridCoord()
                    self.updateGrid()

                    self.axesxSelectModel.clearSelection()
                    self.axesySelectModel.clearSelection()
                    for i in range(self.axesyModel.rowCount()):
                        if self.axesyModel.item(i, 0).data() is item:
                            topleftindex = self.axesyModel.index(
                                i, 0)  # self.axesxModel.item(i,0).index()
                            rightbottomindex = self.axesyModel.index(i, 1)
                            self.axesySelectModel.select(
                                QItemSelection(topleftindex, rightbottomindex),
                                QItemSelectionModel.Select)
                            self.axesyModel.item(i, 0).setText("y:{}".format(
                                item.pos().y()))
                            break

    def mouseReleaseEvent(self, event):
        super().mouseReleaseEvent(event)
        ptscene = self.mapToScene(event.pos())
        # item = self.scene.itemAt(ptscene, self.transform())
        clicked = True if event.pos() == self.__pressPt else False
        if self.mode is OpMode.select:
            pass
            # super().mousePressEvent(event)
        elif self.mode is OpMode.axesx and clicked:
            self.axesxSelectModel.clear()
            self.axesySelectModel.clear()
            for axisitem in self.axesxObjs:
                if abs(ptscene.x() - axisitem.pos().x()) < 5:
                    self.scene.clearSelection()
                    axisitem.setSelected(True)
                    return
            item = QGraphicsAxesItem(
                0,
                self.scene.sceneRect().y(), 0,
                self.scene.sceneRect().y() + self.scene.sceneRect().height())
            item.setPos(ptscene.x(), 0)
            item.axis = "x"
            item.setPen(QPen(Qt.red, 1, Qt.DashLine))
            self.scene.addItem(item)
            item.setFlags(QGraphicsItem.ItemIsSelectable
                          | QGraphicsItem.ItemIsFocusable
                          | QGraphicsItem.ItemIsMovable)

            xs = list(self.axesxObjs.values())
            if not self.axesxObjs:
                nextx = 0
            elif len(self.axesxObjs) == 1:
                nextx = xs[0] + 0.1
            else:
                nextx = 2 * xs[-1] - xs[-2]
            x, okPressed = QInputDialog.getDouble(
                self,
                self.tr("set x coordiniate"),
                self.tr("set the x coord for axis"),
                nextx,
                decimals=3)
            if okPressed and x not in self.axesxObjs.values():
                self.axesxObjs[item] = x
                labelItem = QStandardItem("x:{}".format(item.pos().x()))
                labelItem.setData(item)
                self.axesxModel.appendRow([labelItem, QStandardItem(str(x))])
                self.calGridCoord("x")
                self.updateGrid()
                # item.setSelected(True)
                for i in range(self.axesxModel.rowCount()):
                    if self.axesxModel.item(i, 0).data() is item:
                        topleftindex = self.axesxModel.index(
                            i, 0)  # self.axesxModel.item(i,0).index()
                        rightbottomindex = self.axesxModel.index(i, 1)
                        self.axesxSelectModel.select(
                            QItemSelection(topleftindex, rightbottomindex),
                            QItemSelectionModel.Select)
                        break
                self.sigModified.emit(True)
                self.scene.clearSelection()
            else:
                self.scene.removeItem(item)
        elif self.mode is OpMode.axesy and clicked:
            self.axesxSelectModel.clear()
            self.axesySelectModel.clear()
            for axisitem in self.axesyObjs:
                if abs(ptscene.y() - axisitem.pos().y()) < 5:
                    self.scene.clearSelection()
                    axisitem.setSelected(True)
                    return
            item = QGraphicsAxesItem(
                self.scene.sceneRect().x(), 0,
                self.scene.sceneRect().x() + self.scene.sceneRect().width(), 0)
            item.setPos(0, ptscene.y())
            item.axis = "y"
            item.setPen(QPen(Qt.red, 1, Qt.DashLine))
            self.scene.addItem(item)
            item.setFlags(QGraphicsItem.ItemIsSelectable
                          | QGraphicsItem.ItemIsFocusable
                          | QGraphicsItem.ItemIsMovable)

            ys = list(self.axesyObjs.values())
            if not self.axesyObjs:
                nexty = 0
            elif len(self.axesyObjs) == 1:
                nexty = ys[0] + 0.1
            else:
                nexty = 2 * ys[-1] - ys[-2]
            y, okPressed = QInputDialog.getDouble(
                self,
                self.tr("set y coordiniate"),
                self.tr("set the y coord for axis"),
                nexty,
                decimals=3)
            if okPressed and y not in self.axesyObjs.values():
                self.axesyObjs[item] = y
                labelItem = QStandardItem("y:{}".format(item.pos().y()))
                labelItem.setData(item)
                self.axesyModel.appendRow([labelItem, QStandardItem(str(y))])
                self.calGridCoord("y")
                self.updateGrid()
                # item.setSelected(True)

                for i in range(self.axesyModel.rowCount()):
                    if self.axesyModel.item(i, 0).data() is item:
                        topleftindex = self.axesyModel.index(
                            i, 0)  # self.axesxModel.item(i,0).index()
                        rightbottomindex = self.axesyModel.index(i, 1)
                        self.axesySelectModel.select(
                            QItemSelection(topleftindex, rightbottomindex),
                            QItemSelectionModel.Select)
                        break
                self.sigModified.emit(True)
            else:
                self.scene.removeItem(item)
        elif self.mode is OpMode.curve and clicked:
            self.sigMouseMovePoint.emit(event.pos(), ptscene)
            if len(self.curveObjs) == 0:
                self.addCurve('curve1')
            if self.currentCurve not in self.pointObjs:
                self.pointObjs[self.currentCurve] = []

            ptitem = QGraphicsPointItem()
            ptitem.pointColor = Qt.blue
            ptitem.linewidth = 1
            ptitem.setPos(ptscene)
            ptitem.parentCurve = self.currentCurve
            ptitem.setFlags(QGraphicsItem.ItemIsSelectable
                            | QGraphicsItem.ItemIsFocusable
                            | QGraphicsItem.ItemIsMovable)
            self.scene.addItem(ptitem)

            i = pointInsertPosition(ptitem, self.pointObjs[self.currentCurve])
            self.pointObjs[self.currentCurve].insert(i, ptitem)
            self.updateCurve(self.currentCurve, Qt.red)
            self.sigModified.emit(True)
            ptitem.setSelected(True)

            # item1=QGraphicsRectItem(rect)  #创建矩形---以场景为坐标
            # item1.setFlags(QGraphicsItem.ItemIsSelectable|QGraphicsItem.ItemIsFocusable|QGraphicsItem.ItemIsMovable)  #给图元设置标志
            # QGraphicsItem.ItemIsSelectable---可选择
            # QGraphicsItem.ItemIsFocusable---可设置焦点
            # QGraphicsItem.ItemIsMovable---可移动
            # QGraphicsItem.ItemIsPanel---
            # self.scene.addItem(item1)  #给场景添加图元

    def slotSceneSeletChanged(self):
        items = self.scene.selectedItems()
        if len(items) != 1:  # ony allow select one item
            self.scene.clearSelection()
            return
        # item = items[0]
        item = self.scene.focusItem()
        if not item:
            items = self.scene.selectedItems()
            if len(items) != 1:
                return
            else:
                item = items[0]
        if item:
            if isinstance(item, QGraphicsPointItem) and item.parentCurve:
                self.changeCurrentCurve(item.parentCurve)

    def deleteItem(self, item):
        """delete point on curve or axis object"""
        curvechange = None
        if isinstance(item, QGraphicsPointItem):
            for curvename, pointitems in self.pointObjs.items():
                for ptitem in pointitems:
                    if ptitem is item:
                        curvechange = curvename
                        pointitems.remove(ptitem)
                        self.scene.removeItem(item)
                        self.sigModified.emit(True)
                        break
        if curvechange:
            self.updateCurve(curvechange)

        if isinstance(item, QGraphicsAxesItem):
            if item.axis == "x":
                for line in self.axesxObjs:
                    if line is item:
                        for i in range(self.axesxModel.rowCount()):
                            if self.axesxModel.item(i, 0).data(
                            ) is item:  # float(self.axesxModel.item(i, 0).text().strip("x:")) == line.pos().x():
                                self.axesxModel.removeRow(i)
                                break
                        self.axesxObjs.pop(line)
                        self.scene.removeItem(line)
                        self.sigModified.emit(True)
                        break
            elif item.axis == "y":
                for line in self.axesyObjs:
                    if line is item:
                        for i in range(self.axesyModel.rowCount()):
                            if float(
                                    self.axesyModel.item(i, 0).text().strip(
                                        "y:")) == line.pos().y():
                                self.axesyModel.removeRow(i)
                                break
                        self.axesyObjs.pop(line)
                        self.scene.removeItem(line)
                        self.sigModified.emit(True)
                        break
            self.calGridCoord()
            self.updateGrid()

    def deleteSelectedItem(self):
        pointitems = self.scene.selectedItems()
        if len(pointitems) == 1:
            self.deleteItem(pointitems[0])

    def setGraphImage(self, imgfile):
        if not isinstance(imgfile, str):
            img = QPixmap(300, 400)
            img.fill(QColor("#EEE"))
            self.graphicsPixmapItem.setPixmap(img)
            self.scene.setSceneRect(0, 0, 300, 400)
        elif os.path.exists(imgfile):
            img = QPixmap(imgfile)
            self.graphicsPixmapItem.setPixmap(img)
            self.scene.setSceneRect(0, 0, img.width(), img.height())
            self.scene.clearSelection()  # 【清除选择】
            self.sigModified.emit(True)
            return True
        else:
            return False

    def scaleGraphImage(self, scale=1):
        if scale and scale > 0:
            self.proj.imgScale = scale
        self.graphicsPixmapItem.setScale(scale)
        if self.graphicsPixmapItem.pixmap().width(
        ) > 0 and self.graphicsPixmapItem.pixmap().height() > 0:
            self.scene.setSceneRect(
                0, 0,
                self.graphicsPixmapItem.pixmap().width() * scale,
                self.graphicsPixmapItem.pixmap().height() * scale)
        self.scene.clearSelection()  # 【清除选择】
        self.sigModified.emit(True)

    def addCurve(self, name=None):
        if not name:
            name = nextName(self.currentCurve)
        while (name in self.curveObjs):
            name = nextName(name)
        self.curveObjs[name] = []
        self.pointObjs[name] = []
        item1 = IconItem()
        item2 = QStandardItem(name)
        item3 = QStandardItem()
        item1.setEditable(False)
        item3.setCheckable(True)
        item3.setAutoTristate(False)
        item3.setEditable(False)
        item3.setCheckState(Qt.Checked)
        item1.setTextAlignment(Qt.AlignCenter)
        item2.setTextAlignment(Qt.AlignCenter)
        item3.setTextAlignment(Qt.AlignCenter)
        self.curveModel.appendRow([item1, item2, item3])
        self.changeCurrentCurve(name)
        self.sigModified.emit(True)
        self.sigNewCurveAdded.emit()

    def renameCurve(self, newname=None, name=None):
        if name not in self.curveObjs:
            name = self.currentCurve
        if not newname:
            newname, okPressed = QInputDialog.getText(
                self, self.tr("change curve name"),
                self.tr("Curve to be renamed:{}".format(name)),
                QLineEdit.Normal, name)
        if okPressed and newname != '':
            if newname != name:
                self.curveObjs[newname] = self.curveObjs.pop(name)
                self.pointObjs[newname] = self.pointObjs.pop(name)
                for i in range(self.curveModel.rowCount()):
                    item = self.curveModel.item(i, 1)
                    if item.text() == name:
                        item.setText(newname)
                        self.sigModified.emit(True)
                self.changeCurrentCurve(newname)

    def delCurve(self, name=None):
        if name is None:
            name = self.currentCurve
        if name not in self.curveObjs:
            return
        try:
            self.curveObjs.pop(name)
            self.pointObjs.pop(name)

            for i in range(self.curveModel.rowCount()):
                item = self.curveModel.item(i, 1)
                if item.text() == name:
                    self.curveModel.removeRows(i, 1)
                    self.sigModified.emit(True)
            if len(self.curveObjs) > 0:
                self.changeCurrentCurve(list(self.curveObjs.keys())[0])
            else:
                self.currentCurve = None
        except:
            pass

    def showCurve(self, curvename, visible=True):
        for pts in self.pointObjs[curvename]:
            pts.setVisible(visible)
        for line in self.curveObjs[curvename]:
            line.setVisible(visible)

    def updateCurve(self, name, color=Qt.black):
        # if name in self.curveObjs:
        #     curveitem = self.curveObjs[name]
        # else:
        #     curveitem = QGraphicsPathItem()
        #     self.scene.addItem(curveitem)
        # # path=curveitem.path()
        # path = QPainterPath()
        #
        # pointItems = self.curvePointObjs[name]
        # if len(pointItems) > 0:
        #     path.moveTo(pointItems[0].pos())
        # for pointitem in pointItems[1:]:
        #     path.lineTo(pointitem.pos())
        # curveitem.setPath(path)
        # curveitem.update(curveitem.boundingRect())
        # curveitem.prepareGeometryChange()
        # self.scene.update()
        # self.viewport().repaint()
        # self.viewport().update()

        if not isinstance(name, str):
            return
        if name not in self.pointObjs:
            return

        lastitems = []
        if name in self.curveObjs:
            lastitems = self.curveObjs[name]
            if not isinstance(lastitems, list):
                lastitems = []

        if name in self.pointObjs:
            pointItems = self.pointObjs[name]
        else:
            pointItems = []
        points = []
        for ptitem in pointItems:
            points.append(ptitem.pos())

        self.curveObjs[name] = []
        if len(points) > 1:
            for i in range(1, len(points)):
                l = QGraphicsLineItem(points[i - 1].x(), points[i - 1].y(),
                                      points[i].x(), points[i].y())
                l.setPen(color)
                l.setZValue(10)
                # l.setFlag(QGraphicsItem.ItemIsSelectable)
                self.curveObjs[name].append(l)
                self.scene.addItem(l)

        for line in lastitems:
            self.scene.removeItem(line)

        self.updateCurvePoints(name)

    def showAxes(self, visible=True):
        if visible:
            for line in self.axesxObjs:
                line.setVisible(True)
            for line in self.axesyObjs:
                line.setVisible(True)
        else:
            for line in self.axesxObjs:
                line.setVisible(False)
            for line in self.axesyObjs:
                line.setVisible(False)

    def showGrid(self, visible=True):
        if visible:
            for line in self.gridObjs:
                line.setVisible(True)
        else:
            for line in self.gridObjs:
                line.setVisible(False)

    def calGridCoord(self, mode="all"):
        """calc the coord and pixel position of gridx list and gridy list"""

        if mode in ("x", "all") and len(self.axesxObjs) >= 2:
            axesxcoord = list(self.axesxObjs.values())
            xmin = min(axesxcoord) if self.proj.gridx[0] is None else min(
                self.proj.gridx[0], min(axesxcoord))
            xmax = max(axesxcoord) if self.proj.gridx[1] is None else max(
                self.proj.gridx[1], max(axesxcoord))
            if self.proj.gridx[2] is None:
                if len(self.axesxObjs) == 2:
                    xstep = (xmax - xmin) / 5
                else:
                    axesStep = round(abs(axesxcoord[1] - axesxcoord[0]), 5)
                    for i in range(2, len(axesxcoord)):
                        st = round(abs(axesxcoord[i] - axesxcoord[i - 1]), 5)
                        if axesStep > st:
                            axesStep = st
                    xstep = axesStep
            else:
                xstep = self.proj.gridx[2]
            n = int(round((xmax - xmin) / xstep, 0)) + 1
            gridxcoord = list(np.linspace(xmin, xmax, n))
        else:
            gridxcoord = []

        if mode in ("y", "all") and len(self.axesyObjs) >= 2:
            axesycoord = list(self.axesyObjs.values())
            ymin = min(axesycoord) if self.proj.gridy[0] is None else min(
                self.proj.gridy[0], min(axesycoord))
            ymax = max(axesycoord) if self.proj.gridy[1] is None else max(
                self.proj.gridy[1], max(axesycoord))
            if self.proj.gridy[2] is None:
                if len(self.axesyObjs) == 2:
                    ystep = (ymax - ymin) / 5
                else:
                    axesStep = round(abs(axesycoord[1] - axesycoord[0]), 5)
                    for i in range(2, len(axesycoord)):
                        st = round(axesycoord[i] - axesycoord[i - 1], 5)
                        if axesStep > st:
                            axesStep = st
                    ystep = axesStep
            else:
                ystep = self.proj.gridy[2]

            n = int(round((ymax - ymin) / ystep, 0)) + 1
            gridycoord = list(np.linspace(ymin, ymax, n))
            # gridycoord = list(np.arange(ymin, ymax, ystep)) + [ymax]
        else:
            gridycoord = []

        xpos, ypos = self.coordToPoint(gridxcoord, gridycoord)
        if mode in ["x", "all"]:
            self.gridxpos = xpos
        if mode in ["y", "all"]:
            self.gridypos = ypos
        return

    def updateGrid(self):
        for line in self.gridObjs:
            self.scene.removeItem(line)

        if self.gridxpos and self.gridypos:
            clr = QColor(self.proj.gridColor)
            clr.setAlphaF(self.proj.gridOpacity)
            for x in self.gridxpos:
                line = QGraphicsLineItem(x, self.gridypos[0], x,
                                         self.gridypos[-1])
                line.setZValue(5)
                line.setPen(
                    QPen(clr, self.proj.gridLineWidth, self.proj.gridLineType))
                self.gridObjs.append(line)
                self.scene.addItem(line)
            for y in self.gridypos:
                line = QGraphicsLineItem(self.gridxpos[0], y,
                                         self.gridxpos[-1], y)
                line.setZValue(5)
                line.setPen(
                    QPen(clr, self.proj.gridLineWidth, self.proj.gridLineType))
                self.gridObjs.append(line)
                self.scene.addItem(line)

    def updateCurvePoints(self, name):
        extra = len(self.pointObjs[name]) - self.pointsModel.rowCount()
        if extra > 0:
            for i in range(extra):
                item1 = QStandardItem()
                item2 = QStandardItem()
                item3 = QStandardItem()
                item2.setEditable(False)
                item3.setEditable(False)
                self.pointsModel.appendRow([item1, item2, item3])
        elif extra < 0:
            j = self.pointsModel.rowCount()
            i = j + extra
            self.pointsModel.removeRows(i, -extra)

        for i in range(self.pointsModel.rowCount()):
            pt = self.pointObjs[name][i]
            self.pointsModel.item(i, 0).setText(str(i + 1))
            xlist, ylist = self.pointToCoord([pt.x()], [pt.y()])
            self.pointsModel.item(i, 1).setText(str(round(xlist[0], 6)))
            self.pointsModel.item(i, 2).setText(str(round(ylist[0], 6)))

    def calcCurveCoords(self):
        """calculate datas for export"""
        data = {}
        for curve in self.pointObjs:
            data[curve] = ([], [])
            for item in self.pointObjs[curve]:
                data[curve][0].append(item.x())
                data[curve][1].append(item.y())
            data[curve] = self.pointToCoord(data[curve][0], data[curve][1])
        return data

    def axisvalid(self):
        """if there are axes with same coord value,
        or if there are axes at the save position,
        return False"""
        a = len(self.axesxObjs)
        b = len(set(self.axesxObjs.values()))
        if a != b:
            return False
        a = len(self.axesyObjs)
        b = len(set(self.axesyObjs.values()))
        if a != b:
            return False
        xs = []
        for item in self.axesxObjs:
            xs.append(item.pos().x())
        ys = []
        for item in self.axesyObjs:
            ys.append(item.pos().y())
        a = len(xs)
        b = len(set(xs))
        if a != b:
            return False
        a = len(ys)
        b = len(set(ys))
        if a != b:
            return False
        return True

    def exportToCSVtext(self):
        """return text in csv format, like following:
        curve1
        x,1,2,3
        y,2,3,4
        """
        text = ""
        data = self.calcCurveCoords()
        for curve in data:
            text += curve
            text += "\nx,"
            for x in data[curve][0]:
                text += str(x) + ','
            text += "\ny,"
            for y in data[curve][1]:
                text += str(y) + ','
            text += "\n"
        return text

    def changeCurrentCurve(self, name=None):
        self._lastCurve = self.currentCurve
        if name is None:
            name = self.currentCurve
            if name is None:
                return
        for i in range(self.curveModel.rowCount()):
            if self.curveModel.item(i, 1).text() == name:
                self.curveModel.item(i, 0).switch(True)
            else:
                self.curveModel.item(i, 0).switch(False)

        self.currentCurve = name
        self.updateCurve(self._lastCurve)
        self.updateCurve(self.currentCurve, Qt.red)
        self.updateCurvePoints(name)

    def changeCurveVisible(self, item):
        if item.index().column() == 2:
            visible = item.checkState()
            curvename = self.curveModel.item(item.index().row(), 1).text()
            self.showCurve(curvename, visible)

    def changePointOrder(self, item):
        row = item.row()
        if item.column() != 0:
            return
        newindex = item.text()
        if not newindex.isdigit():
            return
        newindex = int(newindex)
        if newindex == row + 1:
            return
        if newindex > self.pointsModel.rowCount():
            newindex = self.pointsModel.rowCount()
        newindex -= 1

        self.pointObjs[self.currentCurve].insert(
            newindex, self.pointObjs[self.currentCurve].pop(row))
        self.updateCurve(self.currentCurve)
        self.updateCurvePoints(self.currentCurve)

    # def curvetabChanged(self, item):
    #     i = item.row()
    #     j = item.column()
    #     if j == 0:
    #         if item.checkState() is Qt.Checked:
    #             self.curveModel.item(i,0).setCheckState(Qt.Checked)
    #             return
    #         else:
    #             for k in range(self.curveModel.rowCount()):
    #                 if k == i:
    #                     #self.curveModel.item(k,0).setCheckState(Qt.Checked)
    #                     newcurrent = self.curveModel.item(k,1).text()
    #                     print(self.curveModel.item(k,0).checkState())
    #                 else:
    #                     self.curveModel.item(k,0).setCheckState(Qt.Unchecked)
    #     if newcurrent and newcurrent != self.currentCurve:
    #         self.changeCurrentCurve(newcurrent)

    def pointToCoord(self, xlist, ylist):
        if len(self.axesxObjs) >= 2:
            gridxs = []
            for item in self.axesxObjs:
                gridxs.append(item.pos().x())
            coordx = list(self.axesxObjs.values())
            xCoords = interp(gridxs, coordx, xlist)
        else:
            xCoords = xlist

        if len(self.axesyObjs) >= 2:
            gridys = []
            for item in self.axesyObjs:
                gridys.append(item.pos().y())
            coordy = list(self.axesyObjs.values())
            yCoords = interp(gridys, coordy, ylist)
        else:
            yCoords = ylist

        return (xCoords, yCoords)

    def coordToPoint(self, xlist, ylist):
        if len(self.axesxObjs) >= 2:
            gridxpos = []
            for item in self.axesxObjs:
                gridxpos.append(item.pos().x())
            coordx = list(self.axesxObjs.values())
            xposs = interp(coordx, gridxpos, xlist)
        else:
            xposs = xlist

        if len(self.axesyObjs) >= 2:
            gridypos = []
            for item in self.axesyObjs:
                gridypos.append(item.pos().y())
            coordy = list(self.axesyObjs.values())
            yposs = interp(coordy, gridypos, ylist)
        else:
            yposs = ylist

        return (xposs, yposs)
コード例 #9
0
ファイル: listModel.py プロジェクト: ilastik/ilastik
class ListModel(QAbstractTableModel):
    orderChanged = pyqtSignal()
    elementSelected = pyqtSignal(int)


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

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

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

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

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

        def onSelectionChanged(selected, deselected):


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

        self._selectionModel.selectionChanged.connect(onSelectionChanged)

        self._allowRemove = True
        self._toolTipSuffixes = {}

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

        self.unremovable_rows.append(rowIndex)

    def makeRowRemovable(self, rowIndex):
        """
        :param rowIndex: is the index for the label of interest in the current gui setting
        """
        self.unremovable_rows.remove(rowIndex)

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

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

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

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

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

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

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

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

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

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

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



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

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

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

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

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

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

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

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

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

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

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

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

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

        return False

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

    def clearSelectionModel(self):
        self._selectionModel.clear()
コード例 #10
0
ファイル: layerstack.py プロジェクト: ilastik/volumina
class LayerStackModel(QAbstractListModel):
    canMoveSelectedUp = pyqtSignal("bool")
    canMoveSelectedDown = pyqtSignal("bool")
    canDeleteSelected = pyqtSignal("bool")

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

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

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

        self.layerRemoved.connect(_handleRemovedLayer)

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

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

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

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

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

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

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

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

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

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

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

        self.updateGUI()

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

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

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

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

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

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

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

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

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

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

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

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

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

    def insertRows(self, row, count, parent=QModelIndex()):
        """Insert empty rows in the stack.

        DO NOT USE THIS METHOD TO INSERT NEW LAYERS!
        Always use the insert() or append() method.

        """
        if parent.isValid():
            return False
        oldRowCount = self.rowCount()
        # for some reason, row can be negative!
        beginRow = max(0, row)
        endRow = min(beginRow + count - 1, len(self._layerStack))
        self.beginInsertRows(parent, beginRow, endRow)
        while beginRow <= endRow:
            self._layerStack.insert(row, Layer(datasources=[]))
            beginRow += 1
        self.endInsertRows()
        assert self.rowCount() == oldRowCount + 1, "oldRowCount = %d, self.rowCount() = %d" % (
            oldRowCount,
            self.rowCount(),
        )
        return True

    def removeRows(self, row, count, parent=QModelIndex()):
        """Remove rows from the stack.

        DO NOT USE THIS METHOD TO REMOVE LAYERS!
        Use the deleteSelected() method instead.

        """

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

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

    def supportedDropActions(self):
        return Qt.MoveAction

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

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

    def setData(self, index, value, role=Qt.EditRole):
        """Replace one layer with another.

        DO NOT USE THIS METHOD TO INSERT NEW LAYERS!
        Use deleteSelected() followed by insert() or append().

        """
        if role == Qt.EditRole:
            layer = value
            if not isinstance(value, Layer):
                layer = value
            self._layerStack[index.row()] = layer
            self.dataChanged.emit(index, index)
            return True
        elif role == Qt.ToolTipRole:
            self._layerStack[index.row()].setToolTip()
            return True
        return False

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

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

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

    def _onSelectionChanged(self, selected, deselected):
        for idx in deselected.indexes():
            self[idx.row()].setActive(False)
        for idx in selected.indexes():
            self[idx.row()].setActive(True)