обновлять ListView при обновлении списка Python - PullRequest
2 голосов
/ 02 июля 2019

Я пытаюсь использовать PySide для обновления ListView в QML на основе данных из файла CSV.Файл 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 }
        }


    }

Asзаявил, что я могу получить список для печати после того, как он будет импортирован из CSV-файла.Я также могу «предварительно загрузить» список, добавив элементы вроде этого:

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

И, опередив «engine.rootContext (). SetContextProperty (« myModel », model)», я могу увидеть список«foo» и «bar», но ничего не происходит при нажатии моей кнопки для запуска циклов.

Как мне обновить ListView при обновлении statusOutput?

1 Ответ

1 голос
/ 03 июля 2019

Вы объединяете множество элементов, нарушая принцип Одиночной ответственности , который указывает, что каждый класс должен иметь определенную функцию.

В этом случае я создал только 2 класса:

  • FileWorker - это QObject, который живет в другом потоке и читает файл, передающий сигнал с информацией.

  • FileManager - это QObject, который доступен для QMLи имеет в качестве свойства модель, также имеет слот, который позволяет перезагрузить данные.

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

mainfile.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 }
    }
}
...