如何将项目从一个QTreeWidget移到另一个QTreeWidget并跟踪信息?

时间:2019-05-01 18:54:58

标签: python python-3.x pyqt5 qtreewidget

我有一个GUI,用户可以从列表中选择气体成分并将其移至“选择”,另一个按钮从“选择”中获取文本,该列具有以下列:气体成分,分子量,摩尔%。前两列从我创建的字典中获取信息,最后一列是用户输入。

单击按钮并将所有值都填充在“选择”中时,它将要求用户输入数字n = 1-6,然后将在“选择”中获取n%摩尔%最大的行,并在其中选择n行结果并在“结果”的第一栏中添加n最高摩尔%值的“气体成分”文本

我目前正在使用词典来跟踪信息。

    def calculategas(self):
        #makes sure dictionaries are clear for any errors on rerunning button
        self.sortedmol.clear()
        self.componentDic1.clear()
        self.componentDic2.clear()
        self.componentDic3.clear()
        self.mmDict.clear()
        self.mfDict.clear()
        self.mDict.clear()
        self.massFracDict.clear()
        self.molarmassDict.clear()
        self.item_.clear()
        self.lookup.clear()

        root = self.chosen.invisibleRootItem()
        child_count = root.childCount()
        for i in range(child_count):
            item = root.child(i)
            #Takes text from self.chosen QTreeWidget (Top-right)
            component = item.text(0)
            molWeight = item.text(1)
            componentMol = float(item.text(2))
            #creates dictionary of items in self.chosen
            self.componentDic1[component] = componentMol
            self.componentDic2[molWeight] = componentMol
            #Sorts dictionaries above from highest to lowest
            self.sortedmol = dict(sorted(self.componentDic1.items(), key=operator.itemgetter(1),
                                    reverse=True)) # Sorted component list - largest to smallest mol%
            self.sortedmolar = dict(sorted(self.componentDic2.items(), key=operator.itemgetter(1),
                                      reverse=True))  # Sorted molar mass list - largest to smallest mol%
            # change values of self.sortedmol with keys of self.sortedmolar
            self.lookup = {v:k for k, v in self.sortedmol.items()}
            self.componentDic3 = {self.lookup[v]: float(k) for k, v in self.sortedmolar.items()}
            ##Copies so original doesn't change
            self.mmDict = self.sortedmol.copy()
            self.mfDict = self.mmDict.copy()
            self.mDict = self.componentDic3.copy()

            ###Calculations
            self.molarmassDict = {k: round(v * self.mmDict[k] / 100, 3) for k, v in self.mDict.items() if
                                  k in self.mmDict}
            summolmDict = round(sum(self.molarmassDict.values()), 3)
            self.massFracDict = {k: round(self.molarmassDict[k] / summolmDict, 3) for k, v in self.molarmassDict.items()
                                 if
                                 k in self.molarmassDict}
        componentNum, ok = QInputDialog.getText(None, 'Number of components', 'How many components do you wish to use?')
        if (ok):
            #Remove any items in result QTreeWidget
            current_item = self.result.invisibleRootItem()
            children = []
            for child in range(current_item.childCount()):
                children.append(current_item.child(child))
            for child in children:
                current_item.removeChild(child)
            #Adds rows to self.result QTreeWidget
            for i in range(int(componentNum)):
                self.item_[i] = QtWidgets.QTreeWidgetItem(self.result)
                self.item_[i].setFlags(
                    QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled)
            if len(self.sortedmol) > int(componentNum):  # takes only # of components user wants
                ##Adds the number of components user inputs with highest mol% to self.result
                root = self.result.invisibleRootItem()
                child_count = root.childCount()
                for i in range(child_count):
                    item = root.child(i)
                    item.setText(0, str(list(self.massFracDict)[i]))  # update first column with dictionary keys
            else:
                ###This section will change
                root = self.result.invisibleRootItem()
                child_count = root.childCount()
                for i in range(child_count):
                    item = root.child(i)
                    item.setText(0, str(list(self.massFracDict)[i]))  # update first column with dictionary keys

当前一切正常,除了当摩尔%或分子量存在重复值时,它往往会跳过它。

字典self.sortedmol:

键=气体成分文本

值= mol%文本

字典自分类摩尔:

键=分子量文本

值= mol%文本

问题:

  • 如果两种成分的分子量相同,它将被忽略

  • 如果两种成分的摩尔百分比相同,它将被忽略

    • 总体目标:在“结果”中将“选择的”中具有最高mol%的n行添加到“结果”,并保留值以供以后计算。

    • 问题:是否有任何方法可以解决此错误,或者使用其他方法来获得相同的预期结果?

第一步:

enter image description here

第二步:

enter image description here

1 个答案:

答案 0 :(得分:1)

如果要获得mol%最高的n行,则必须使用QSortFilterProxyModel进行排序,并使用另一个进行过滤。

from PyQt5 import QtCore, QtGui, QtWidgets

ListRole = QtCore.Qt.UserRole
VisibleRole = QtCore.Qt.UserRole + 1


class TopProxyModel(QtCore.QSortFilterProxyModel):
    @property
    def number(self):
        if not hasattr(self, "_number"):
            self._number = -1
        return self._number

    @number.setter
    def number(self, number):
        self._number = number
        self.invalidateFilter()

    def filterAcceptsRow(self, sourceRow, sourceParent):
        if self.number < 0:
            ix = self.sourceModel().index(sourceRow, 2)
            self.sourceModel().setData(ix, False, VisibleRole)
            return True
        return sourceRow < self.number


class BlankDelegate(QtWidgets.QStyledItemDelegate):
    def initStyleOption(self, option, index):
        super().initStyleOption(option, index)
        if not index.data(VisibleRole):
            option.text = ""

    def setModelData(self, editor, model, index):
        sm = model
        ix = index
        while hasattr(sm, "sourceModel"):
            ix = sm.mapToSource(ix)
            sm = sm.sourceModel()
        sm.setData(ix, editor.value(), QtCore.Qt.DisplayRole)
        if not sm.data(ix, VisibleRole):
            sm.setData(ix, True, VisibleRole)


class ReadOnlyDelegate(QtWidgets.QStyledItemDelegate):
    def createEditor(self, parent, option, index):
        return None


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        datas = [
            ("IsonButane", 58.12, 13),
            ("IsonPentane", 75.12, 3),
            ("Methane", 16.04, 5),
            ("Nitrogen", 28.01, 5),
            ("Hexane", 86.17, 5),
            ("Hydrogen", 2.02, 13),
            ("Hydrogen Sulfide", 34.08, 2),
        ]

        add_button = QtWidgets.QPushButton(">>>", clicked=self.add_row)
        remove_button = QtWidgets.QPushButton("<<<", clicked=self.remove_row)
        select_button = QtWidgets.QPushButton("Calculate", clicked=self.select)
        sp = select_button.sizePolicy()
        sp.setHorizontalPolicy(QtWidgets.QSizePolicy.Maximum)
        select_button.setSizePolicy(sp)

        self.listwidget = QtWidgets.QListWidget(
            selectionMode=QtWidgets.QAbstractItemView.MultiSelection
        )
        for data in datas:
            item = QtWidgets.QListWidgetItem(data[0])
            item.setData(ListRole, data)
            self.listwidget.addItem(item)
        self.tree_widget = QtWidgets.QTreeWidget(
            columnCount=3,
            indentation=0,
            selectionMode=QtWidgets.QAbstractItemView.MultiSelection,
        )
        for i, T in enumerate(
            (ReadOnlyDelegate, ReadOnlyDelegate, BlankDelegate)
        ):
            delegate = T(self.tree_widget)
            self.tree_widget.setItemDelegateForColumn(2, delegate)

        self.tree_widget.setHeaderLabels(
            ["Gas Component", "Molecular Weight", "Mol%"]
        )
        self.tree_view = QtWidgets.QTreeView(indentation=0)

        proxy_sort = QtCore.QSortFilterProxyModel(self)
        proxy_sort.setSourceModel(self.tree_widget.model())
        proxy_sort.sort(2, QtCore.Qt.DescendingOrder)

        self.proxy_top = TopProxyModel(self)
        self.proxy_top.number = 0
        self.proxy_top.setSourceModel(proxy_sort)
        self.tree_view.setModel(self.proxy_top)

        lay = QtWidgets.QGridLayout(self)
        lay.addWidget(QtWidgets.QLabel("<b>Available Gases:</b>"), 0, 0)
        lay.addWidget(self.listwidget)

        vlay = QtWidgets.QVBoxLayout()
        vlay.addStretch()
        vlay.addWidget(add_button)
        vlay.addWidget(remove_button)
        vlay.addStretch()
        lay.addLayout(vlay, 1, 1)
        lay.addWidget(QtWidgets.QLabel("<b>Chosen Gases</b>"), 0, 2)
        lay.addWidget(self.tree_widget, 1, 2)

        lay.addWidget(select_button, 2, 2, alignment=QtCore.Qt.AlignCenter)
        lay.addWidget(QtWidgets.QLabel("<b>Result:</b>"), 3, 2)
        lay.addWidget(self.tree_view, 4, 2)

    @QtCore.pyqtSlot()
    def add_row(self):
        for item in self.listwidget.selectedItems():
            data = item.data(ListRole)
            text = item.text()
            if self.tree_widget.findItems(text, QtCore.Qt.MatchExactly):
                continue
            it = self.listwidget.takeItem(self.listwidget.row(item))
            item = QtWidgets.QTreeWidgetItem()
            self.tree_widget.addTopLevelItem(item)
            item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
            for i, e in enumerate(data):
                item.setData(i, QtCore.Qt.DisplayRole, e)

    @QtCore.pyqtSlot()
    def remove_row(self):
        rows = [
            self.tree_widget.indexOfTopLevelItem(item)
            for item in self.tree_widget.selectedItems()
        ]
        for row in sorted(rows, reverse=True):
            item = self.tree_widget.takeTopLevelItem(row)
            data = []
            for i in range(self.tree_widget.columnCount()):
                data.append(item.data(i, QtCore.Qt.DisplayRole))
            it = QtWidgets.QListWidgetItem(data[0])
            it.setData(ListRole, data)
            self.listwidget.addItem(it)
            if item is not None:
                del item

    @QtCore.pyqtSlot()
    def select(self):
        last_number = max(self.proxy_top.number, 0)
        number, ok = QtWidgets.QInputDialog.getInt(
            None,
            "Number of components",
            "How many components do you wish to use?",
            last_number,
            min=-1,
            max=self.tree_widget.topLevelItemCount(),
        )
        if ok:
            self.proxy_top.number = number
        for i in range(self.tree_widget.topLevelItemCount()):
            it = self.tree_widget.topLevelItem(i)
            it.setData(2, VisibleRole, False)


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())