Python 列表更新时更新 ListView

Posted

技术标签:

【中文标题】Python 列表更新时更新 ListView【英文标题】:update ListView when Python list updates 【发布时间】:2019-07-02 19:57:35 【问题描述】:

我正在尝试使用 PySide 根据 csv 文件中的数据更新 QML 中的 ListView。 csv 文件是由外部程序更新的,所以我设置了一个循环来从该文件中循环获取数据。

我能够将数据导入 Python 并打印出来,但我认为我的错误是信号/插槽问题,它根本没有在 QML 中更新。

main.py:

def importSimStatus(statusOutput):
    with open(r'status output.csv','r') as readFile:

        dummyList2 = statusOutput.outputStatus

        i = 0

        for j in range(8):
            statusOutput.setOutputStatus("", j)

        csvReader = csv.reader(readFile)
        for row in csvReader:


            statusOutput.setOutputStatus(row[0], i)
            dummyList2 = statusOutput.outputStatus

            i += 1


def checkSimOutput():

    for out in range(8):
        statusOutput.setOutputStatus("", out)

    simResults = []

    dummyList = statusOutput.outputStatus
    while (dummyList[7] == ""):
        try:
            importSimStatus(statusOutput)


        except:
            pass
        time.sleep(1)

        print(statusOutput.outputStatus)

class CheckSimOutput(QRunnable):
    def run(self):
        checkSimOutput()


class OutputData(QObject):

    statusSig = Signal(list)


    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self.m_outputStatus = []        

    def resizeOutputStatus(self, i):
        for x in range(i):
            self.m_outputStatus.append("")

    @Property(list, notify=statusSig)
    def outputStatus(self):
        return self.m_outputStatus

    @outputStatus.setter
    def setOutputStatus(self, text, i):
        if self.m_outputStatus[i] == text:
            return
        self.m_outputStatus[i] = text
        self.statusSig.emit(self.m_outputStatus)

class Settings(QObject):


    simWorkAround = Signal(int)

    def __init__(self, parent=None):
        QObject.__init__(self, parent)

        self.m_simWorkAround = 0

    @Property(int, notify=simWorkAround)
    def simWorkaround(self):
        return self.m_simWorkAround

    @simWorkaround.setter
    def setSimWorkaround(self, num):
        if self.m_simWorkAround == num:
            return
        self.m_simWorkAround = num
        self.simWorkAround.emit(self.m_simWorkAround)

if __name__ == '__main__':

    app = QGuiApplication(sys.argv)

    settings = Settings()
    statusOutput = OutputData()

    statusOutput.resizeOutputStatus(8)

    def simThread():
        simOutRunnable = CheckSimOutput()
        QThreadPool.globalInstance().start(simOutRunnable)


    model = QStringListModel()
    model.setStringList(statusOutput.outputStatus)

    engine = QQmlApplicationEngine()

    engine.rootContext().setContextProperty("settings", settings)
    engine.rootContext().setContextProperty("myModel", model)

    engine.load(QUrl.fromLocalFile('mainfile.qml'))
    if not engine.rootObjects():
        sys.exit(-1)

    settings.simWorkAround.connect(simThread)
    statusOutput.statusSig.connect(model.setStringList(statusOutput.outputStatus))


    sys.exit(app.exec_())

mainfile.qml:

import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.1


ApplicationWindow 

visible: true
width: 640
height: 480
title: qsTr("Main Program")

       Button 
           text: qsTr("Start Draft")
           anchors.top: parent.top
           anchors.topMargin: 21
           anchors.horizontalCenterOffset: 0
           anchors.horizontalCenter: parent.horizontalCenter
           onClicked: settings.simWorkaround = settings.simWorkaround + 1
       



        ListView
            id: listView
            x: 0
            width: 200
            height: 150
            anchors.top: parent.top
            anchors.topMargin: 55
            anchors.horizontalCenter: parent.horizontalCenter
            contentWidth: 0
            model: myModel
                //anchors.fill: parent
            delegate: Text  text: model.display 
        


    

如前所述,我可以在从 csv 文件导入列表后打印该列表。我还可以通过添加这样的项目来“预加载”列表:

statusOutput.setOutputStatus("foo",0)
statusOutput.setOutputStatus("bar",1)

在 "engine.rootContext().setContextProperty("myModel", model)" 之前,我可以看到 "foo" 和 "bar" 的列表,但是当单击我的按钮运行循环时没有任何反应.

如何让 ListView 在 statusOutput 更新时刷新?

【问题讨论】:

您的逻辑有点奇怪,甚至很混乱,您可以更详细地解释您的逻辑的每个部分。 我想我理解的是,当您按下文本时,您想重新阅读 .csv 并在 QML 中显示它,对吗? 抱歉,这令人困惑 - 这是我的第一个 PySide/QML 项目,发现了一些可能不是很好但有效的解决方法(除了这个问题)。是的,这基本上就是正在发生的事情 - 当单击按钮时,程序会循环检查由单独程序输出的 csv 文件。它循环到它读取 csv 文件中的第八行。一切正常,但是当 statusOutput.ouputStatus 更改时 ListView 不会更新 换句话说,您是否希望每次按下按钮时只读取文件的前 8 行?如果这是一种解决方法,那么根本问题是什么?是不是每次修改文件时都不需要按键读取8行? 正确,只有前 8 行是相关的,所以那部分是故意的。我希望每次更改 python 时都显示列表,而不管按钮是否按下。我确实计划保留该按钮,但由于我计划添加其他项目 【参考方案1】:

您正在组合许多打破Single responsibility principle 的元素,该Single responsibility principle 表示每个类都必须具有定义的函数。

在这种情况下,我只创建了 2 个类:

FileWorker 是一个 QObject,它存在于另一个线程中,它读取文件并发出带有信息的信号。

FileManager 是一个向 QML 公开的 QObject,并具有模型的属性,还有一个允许重新加载数据的 Slot。

ma​​in.py:

import os
import csv
from functools import partial
from PySide2 import QtCore, QtGui, QtQml

CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))


class FileWorker(QtCore.QObject):
    linesChanged = QtCore.Signal(list)

    @QtCore.Slot(str)
    def read_csv(self, filename):
        lines = []
        with open(filename, "r") as f:
            csv_reader = csv.reader(f)
            for i, row in enumerate(csv_reader):
                if i > 7:
                    break
                lines.append(row[0])
        self.linesChanged.emit(lines)


class FileManager(QtCore.QObject):
    def __init__(self, parent=None):
        super(FileManager, self).__init__(parent)
        self.m_model = QtCore.QStringListModel(self)
        self.m_thread = QtCore.QThread(self)
        self.m_thread.start()
        self.m_worker = FileWorker()
        self.m_worker.moveToThread(self.m_thread)
        self.m_worker.linesChanged.connect(self.updateModel)

    @QtCore.Property(QtCore.QAbstractItemModel, constant=True)
    def model(self):
        return self.m_model

    @QtCore.Slot()
    def load(self):
        filename = os.path.join(CURRENT_DIR, "status output.csv")
        wrapper = partial(self.m_worker.read_csv, filename)
        QtCore.QTimer.singleShot(0, wrapper)

    def clean(self):
        self.m_thread.quit()
        self.m_thread.wait()

    @QtCore.Slot(list)
    def updateModel(self, lines):
        self.m_model.setStringList(lines)


if __name__ == "__main__":
    import sys

    app = QtGui.QGuiApplication(sys.argv)

    engine = QtQml.QQmlApplicationEngine()

    filemanager = FileManager()
    filemanager.load()

    engine.rootContext().setContextProperty("filemanager", filemanager)
    filename = os.path.join(CURRENT_DIR, "mainfile.qml")
    engine.load(QtCore.QUrl.fromLocalFile(filename))
    if not engine.rootObjects():
        sys.exit(-1)

    res = app.exec_()

    filemanager.clean()

    sys.exit(res)

ma​​infile.qml:

import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Window 2.2


ApplicationWindow 
    visible: true
    width: 640
    height: 480
    title: qsTr("Main Program")

    Button 
        text: qsTr("Start Draft")
        anchors.top: parent.top
        anchors.topMargin: 21
        anchors.horizontalCenterOffset: 0
        anchors.horizontalCenter: parent.horizontalCenter
        onClicked: filemanager.load()
    

    ListView
        id: listView
        width: 200
        height: 150
        anchors.top: parent.top
        anchors.topMargin: 55
        anchors.horizontalCenter: parent.horizontalCenter
        contentWidth: 0
        model: filemanager.model
        // anchors.fill: parent
        delegate: Text  text: model.display 
    

【讨论】:

太棒了,成功了。感谢您帮助新手!

以上是关于Python 列表更新时更新 ListView的主要内容,如果未能解决你的问题,请参考以下文章

如何在python中更新全局变量

如何保存对 Python 列表所做的更改,以便在重新运行脚本后更新? [复制]

如何更新显示的值而不是每次在 Python 中打印?

python常用内置函数学习(持续更新...)

Python学习心得 : 更新列表

如何使用 MySQL executemany 更新 Python 中的列表列表?