如何根据原始列表相同的值但不同的索引创建新列表

Posted

技术标签:

【中文标题】如何根据原始列表相同的值但不同的索引创建新列表【英文标题】:How to create new list based on the original list same values but different index 【发布时间】:2020-11-26 10:32:52 【问题描述】:

我在 Python 中使用 Pyqt5 创建了一个 GUI 界面,它导入一个 Excel 文件并将其列显示为 listWidget 中的项目。用户可以在其中检查项目,每个检查的项目都附加到 new_list

问题是,如果我有以下示例:

original_list = ["a","b","c","d","e","f"]

如果用户为新列表选择以下项目:

“C”、“f”、“a”

结果:

new_list = ["a","c","f"]

预期结果:

new_list =  ["c","f","a"]

在此图像中,用户在索引 0 的项目之前选择索引 3 的项目:

new_list 将索引 0 处的项目附加到索引 3 处的项目之前

代码:

from PyQt5 import QtCore, QtGui, QtWidgets

import os
import pandas as pd
import numpy as np

import chardet




class Ui_MainWindow(QtWidgets.QMainWindow):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(800, 600)

        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        
        self.header_list = QtWidgets.QListWidget(self.centralwidget)
        self.header_list.setMaximumSize(QtCore.QSize(120, 1667))
        self.header_list.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
        self.header_list.setObjectName("header_list")
        self.header_list.itemChanged.connect(self.selectionChanged)
        
        MainWindow.setCentralWidget(self.centralwidget)
        
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 22))
        self.menubar.setObjectName("menubar")

        self.menufile = self.menubar.addMenu("File")
        self.menufile.setObjectName("menufile")

        self.menuimportfile = QtWidgets.QAction("Import File",self.menufile)
        self.menuimportfile.setObjectName("importfile")
        self.menuimportfile.triggered.connect(lambda:self.loadFile())
        
        MainWindow.setMenuBar(self.menubar)
        
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        
        MainWindow.setStatusBar(self.statusbar)
        
        self.menufile.addAction(self.menuimportfile)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.menufile.setTitle(_translate("MainWindow", "file"))


    def loadFile(self):
        try:
            fileName, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Open File", "", "Excel Files (*.csv *.xls *.xlsx)");
            print(fileName)            
            name, ext = os.path.splitext(fileName)
            print("name:  ".format(name))
            print("ext:   ".format(ext))
            if ext == ".csv":
                with open(fileName, 'rb') as rawdata:
                    result = chardet.detect(rawdata.read(100000))
                print(result)
                df = pd.read_csv(fileName,encoding = result["encoding"])
            elif ext == ".xls" or ext == ".xlsx":
                df = pd.read_excel(fileName)
            self.df = df

            
        #part that display items in the qlistWidget
            self.header_list.clear()
            savelist = list(self.df)
            for item in savelist:
                qitem = QtWidgets.QListWidgetItem ( ) 
                qitem.setText ( item ) 
                qitem. setFlags ( QtCore. Qt . ItemIsUserCheckable | QtCore. Qt . ItemIsEnabled ) 
                qitem.setCheckState ( QtCore.Qt.Unchecked ) 
                self.header_list.addItem ( qitem )
        
        except Exception as e:
            print("error is ".format(e))

    def selectionChanged(self):
        self.checked = []
        
        for row in range(self.header_list.count()):
            item = self.header_list.item(row)
            #need to be checked in order to display the item in the order that the user choose.
            if item.checkState():
                if item in self.checked:
                    del self.checked[item]
                else:
                # if not item in self.checked: 
                    self.checked.append(item)
            
                
        # checked.insert(len(checked)-1,item)
        print("Checked items: ", ", ".join(i.text() for i in self.checked))
        self.checked = [i.text() for i in self.checked]




if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

【问题讨论】:

【参考方案1】:

看起来selectionChanged 的实现可以是下面的行。 只需迭代并收集选中的项目。

def selectionChanged(self):
    self.checked = [self.header_list.item(row) for row in range(self.header_list.count()) if self.header_list.item(row).checkState()]

【讨论】:

您的回答与我的问题给出了相同的结果,而不是预期的结果 只需迭代并收集选中的项目。但基于首先选择的项目意味着如果用户首先在 self.check[] 中选择项目索引 3,它将是项目索引 0 是否要收集选中项的索引? 不是索引,但我的意思是:list = ["car","bike","cell"] 这辆车在我的代码中有索引 0 自行车 1 单元格 3,如果用户 check first bike 然后是 car ... self.check=["bike","car"] 到现在为止,显示的结果是用户首先检查的结果,self.check 是从 list 获取值,因为它的索引等于。【参考方案2】:

itemChanged 信号还发送 what 项目已更改,这可用于操作列表。

显然,如果您不断创建一个新列表,则没有简单的方法来跟踪之前选择的内容,因此您需要一个 持久 列表并最终根据它们的检查状态附加/删除项目.

请注意,我对您的代码结构进行了一些更改(稍后会详细介绍)。

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.checked = []

    # ...

    def selectionChanged(self, item):
        if item.checkState():
            if item not in self.checked:
                self.checked.append(item)
        elif item in self.checked:
            self.checked.remove(item)
        print([i.text() for i in self.checked])


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    mainWindow = MainWindow()
    mainWindow.show()
    sys.exit(app.exec_())

除了明显需要在__init__ 中创建一个列表之外,这些修改是由于您使用的可能是对pyuic 生成的文件的修改:这些文件应该永远不要 被修改(也不应该试图模仿他们的行为),应该始终按原样使用,并且只能作为导入文件使用。 即使在这种情况下,您也将 Ui_MainWindow 创建为 QMainWindow 的子类,但实际上并没有这样使用它。这样做并没有实际的好处,它只会让事情变得比他们应该做的更复杂。我的建议是使用 pyuic 重新创建 ui 文件,然后将其导入脚本中,删除当前代码中的 setupUi,并从导入文件的 QMainWindow 和 Ui_MainWindow 中子类化主窗口;假设文件导出为ui_MainWindow.py,您的代码将变为:

from PyQt5 import QtCore, QtGui, QtWidgets
from ui_MainWindow import Ui_MainWindow

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.checked = []
        self.header_list.itemChanged.connect(self.selectionChanged)
# ...

您可以在有关using Designer 的官方指南中阅读有关正确使用这些文件的更多信息。

最后,我强烈建议您使用layout managers,否则如果窗口太小,您的所有小部件将无法使用,如果窗口较大,则会有大量空间未使用。

【讨论】:

感谢您的帮助和解释,但我尝试了您的功能,但它仍然返回相同的结果,而不是预期的结果 @RanyFahed 该代码有效,因此请确保您已正确应用我所做的更改,并且您不会不断地重新创建列表。 你的意思是**你不是不断地重新创建列表** @RanyFahed 正如答案中已经解释的那样,在您的代码中,每次调用 selectionChanged 时,您都会创建一个新的 self.checked 列表。这显然是错误的,因为它不允许您以正确的顺序跟踪先前检查的项目。如果您查看我的代码,self.checked 列表仅在__init__ 中创建一次 我不想跟踪以前检查的项目。我想要的是,一旦我在header_list 中拥有将创建一次self.checked 每次都创建的项目......所以你的意思是self.checked不能每次都创建??

以上是关于如何根据原始列表相同的值但不同的索引创建新列表的主要内容,如果未能解决你的问题,请参考以下文章

如何使用相同的值但使用现有字段之一的不同数据类型来注释 Django Queryset?

如何删除R中两列中具有相同值但ID不同的行[重复]

SwiftUI - 如何根据列表为文本设置不同的值

带有 ggplots 的 for 循环生成具有相同值但标题不同的图形

如何避免从两个不同的随机变量中获得相同的值,但值来自同一个列表?

如何在列表中找到相同的值并将新列表组合在一起?