再次加载数据后如何让QTableView刷新背景色 [英] How to make QTableView refresh Background color after it loads data again

查看:146
本文介绍了再次加载数据后如何让QTableView刷新背景色的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对 QTableView 有以下疑问,我添加了一些代码,根据数据框在最后一列中包含的字符串更改行背景.

i have the following doubt regarding QTableView, i have added some code that changes the row background depending on what string the dataframe contains on the last column.

    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

当表格第一次加载它正确地绘制表格时,问题是我有一个特定的代码部分,它总是每 5 秒运行一次,以便用新信息刷新表格.

When the table loads the first time it properly paints the Table, the thing is that i have a specific part of the code that always runs every 5 seconds as to refresh the Table with fresh information.

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 上的颜色.

The thing is that when the above code runs, the table does in fact update the data, but it does not change the colors. I currently have tried different methods like defining setData and in there update the colors but with no avail. Now im asking you if someone knows something regarding updating colors on a QTableView.

顺便说一下,我在下面附上了 python 程序的完整代码以提供上下文.

By the way, I'm attaching the entire code for the python program below as to give context.

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_())

推荐答案

我了解到您正在使用 threading.Timer() 因为加载和处理数据帧的过程非常耗时,而您想做一个周期性的任务(如果任务不消耗太多时间,那么另一个选择是使用 QTimer)但问题是你正在创建一个模型并添加可见的信息,这是该任务的一部分来自 Qt 禁止的另一个线程的 GUI,如 文档.

I understand that you are using threading.Timer() because the process of loading and processing the dataframe is very time consuming and you want to do a periodic task (if the task does not consume much time then another option is to use QTimer) but the problem is that you are creating a model and adding information in sight that is part of the GUI from another thread which is prohibited by Qt as indicated by the docs.

考虑到上面最好是通过信号将辅助线程的信息发送给主线程,我也实现了一个重置​​模型信息的方法,避免了创建新模型的需要,最后我添加了验证码,以免验证码失败.

Considering the above it is better to send the information of the secondary thread to the main thread through signals, I have also implemented a method that resets the information of the model avoiding the need to create new models, and finally I have added verification code so that the code don't fail.

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)

正如你所看到的,我为我的测试随机创建了数据帧,但如果你想使用你的代码,那么你必须按如下方式替换它:

As you can see I created dataframes randomly for my test but if you want to use your code then you must replace it as follows:

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()

这篇关于再次加载数据后如何让QTableView刷新背景色的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆