Как сделать QTableView refre sh Цвет фона после повторной загрузки данных - PullRequest
1 голос
/ 09 января 2020

У меня есть следующие сомнения относительно QTableView , я добавил некоторый код, который меняет фон строки в зависимости от того, какую строку содержит фрейм данных в последнем столбце.

    def data(self, index, role=Qt.DisplayRole):
        if index.isValid():
            if role == Qt.BackgroundRole:
                if df.iloc[index.row(),5] == "Ready for QC":
                    return QBrush(Qt.yellow)
                if df.iloc[index.row(),5] == "In Progress":
                    return QBrush(Qt.green)
            if role == Qt.DisplayRole:
                return str(self._data.iloc[index.row(), index.column()])
        return None

Когда таблица загружается в первый раз, когда она правильно рисует таблицу, дело в том, что у меня есть определенная c часть кода, которая всегда запускается каждые 5 секунд для обновления sh таблицы с информацией fre sh.

def printit():
    threading.Timer(5.0, printit).start()
    weekNumber = date.today().isocalendar()[1]
    aux = pd.read_excel('PCS tasks 2020.xlsm',sheet_name='W'+str(weekNumber))
    today = datetime.today()
    df = aux[aux['Date Received'] == today.strftime("%Y-%d-%m")]
    df = df[["Requestor","Subject","Task type","Created by","QC Executive","Status"]].fillna("")
    df = df[df['Status'] != "Completed"]
    model = pandasModel(df)
    view.setModel(None)
    view.setModel(model)

Дело в том, что при запуске приведенного выше кода таблица фактически обновляет данные, но не меняет цвета. В настоящее время я пробовал разные методы, такие как определение setData и там обновить цвета, но безрезультатно. Теперь я спрашиваю вас, знает ли кто-нибудь что-то об обновлении цветов в QTableView.

Кстати, я прилагаю весь код для программы python ниже, чтобы дать контекст.

import sys
import pandas as pd
from PyQt5.QtWidgets import QApplication, QTableView
from PyQt5.QtCore import QAbstractTableModel, Qt
from PyQt5.QtGui import QBrush
from datetime import date, datetime
import threading

weekNumber = date.today().isocalendar()[1]
aux = pd.read_excel('PCS tasks 2020.xlsm',sheet_name='W'+str(weekNumber))
today = datetime.today()
df = aux[aux['Date Received'] == today.strftime("%Y-%d-%m")]
df = df[["Requestor","Subject","Task type","Created by","QC Executive","Status"]].fillna("")
df = df[df['Status'] != "Completed"]

def printit():
    threading.Timer(5.0, printit).start()
    weekNumber = date.today().isocalendar()[1]
    aux = pd.read_excel('PCS tasks 2020.xlsm',sheet_name='W'+str(weekNumber))
    today = datetime.today()
    df = aux[aux['Date Received'] == today.strftime("%Y-%d-%m")]
    df = df[["Requestor","Subject","Task type","Created by","QC Executive","Status"]].fillna("")
    df = df[df['Status'] != "Completed"]
    model = pandasModel(df)
    view.setModel(None)
    view.setModel(model)

class pandasModel(QAbstractTableModel):

    def __init__(self, data):
        QAbstractTableModel.__init__(self)
        self._data = data

    def rowCount(self, parent=None):
        return self._data.shape[0]

    def columnCount(self, parent=None):
        return self._data.shape[1] -1

    def data(self, index, role=Qt.DisplayRole):
        if index.isValid():
            if role == Qt.BackgroundRole:
                if df.iloc[index.row(),5] == "Ready for QC":
                    return QBrush(Qt.yellow)
                if df.iloc[index.row(),5] == "In Progress":
                    return QBrush(Qt.green)
            if role == Qt.DisplayRole:
                return str(self._data.iloc[index.row(), index.column()])
        return None

    def headerData(self, col, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self._data.columns[col]
        return None

if __name__ == '__main__':
    app = QApplication(sys.argv)
    model = pandasModel(df)
    view = QTableView()
    view.setModel(model)
    view.resize(523, 300)
    printit()
    view.show()
    sys.exit(app.exec_())

1 Ответ

2 голосов
/ 09 января 2020

Я понимаю, что вы используете threading.Timer(), потому что процесс загрузки и обработки кадра данных занимает много времени, и вы хотите выполнить задачу periodi c (если задача не занимает много времени, тогда другой вариант - используйте QTimer), но проблема в том, что вы создаете модель и добавляете видимую информацию, которая является частью GUI из другого потока, который запрещен Qt, как указано в документах .

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

import sys
import pandas as pd

from PyQt5.QtCore import pyqtSignal, pyqtSlot, QAbstractTableModel, QObject, Qt
from PyQt5.QtGui import QBrush
from PyQt5.QtWidgets import QApplication, QTableView

import threading


class PandasManager(QObject):
    dataFrameChanged = pyqtSignal(pd.DataFrame)

    def start(self):
        self.t = threading.Timer(0, self.load)
        self.t.start()

    def load(self):
        import random

        headers = list("ABCDEFG")
        data = [random.sample(range(255), len(headers)) for _ in headers]

        for d in data:
            d[5] = random.choice(["Ready for QC", "In Progress", "Another option"])

        df = pd.DataFrame(data, columns=headers,)

        self.dataFrameChanged.emit(df)
        self.t = threading.Timer(5.0, self.load)
        self.t.start()

    def stop(self):
        self.t.cancel()


class PandasModel(QAbstractTableModel):
    def __init__(self, df=pd.DataFrame()):
        QAbstractTableModel.__init__(self)
        self._df = df

    @pyqtSlot(pd.DataFrame)
    def setDataFrame(self, df):
        self.beginResetModel()
        self._df = df
        self.endResetModel()

    def rowCount(self, parent=None):
        return self._df.shape[0]

    def columnCount(self, parent=None):
        return self._df.shape[1]

    def data(self, index, role=Qt.DisplayRole):
        if index.isValid():
            if role == Qt.BackgroundRole:
                if self.columnCount() >= 6:
                    it = self._df.iloc[index.row(), 5]
                    if it == "Ready for QC":
                        return QBrush(Qt.yellow)
                    if it == "In Progress":
                        return QBrush(Qt.green)
            if role == Qt.DisplayRole:
                return str(self._df.iloc[index.row(), index.column()])

    def headerData(self, col, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self._df.columns[col]
        return None


if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = QTableView()
    model = PandasModel()
    w.setModel(model)
    w.show()

    manager = PandasManager()
    manager.dataFrameChanged.connect(model.setDataFrame)
    manager.start()

    ret = app.exec_()

    manager.stop()

    sys.exit(ret)

Как вы можете видеть, я случайно создал кадры данных для своего теста, но если вы хотите использовать свой код, вы должны заменить его следующим образом:

def load(self):
    weekNumber = date.today().isocalendar()[1]
    aux = pd.read_excel("PCS tasks 2020.xlsm", sheet_name="W" + str(weekNumber))
    today = datetime.today()
    df = aux[aux["Date Received"] == today.strftime("%Y-%d-%m")]
    df = df[
        [
            "Requestor",
            "Subject",
            "Task type",
            "Created by",
            "QC Executive",
            "Status",
        ]
    ].fillna("")
    df = df[df["Status"] != "Completed"]
    self.dataFrameChanged.emit(df)
    self.t = threading.Timer(5.0, self.load)
    self.t.start()
...