python - 再次加载数据后如何使QTableView刷新背景颜色
问题描述
我对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
当表格第一次加载正确绘制表格时,问题是我有一个特定部分的代码总是每 5 秒运行一次,以便用新信息刷新表格。
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_())
解决方案
我了解您正在使用threading.Timer()
,因为加载和处理数据帧的过程非常耗时,并且您想要执行定期任务(如果任务不消耗太多时间,那么另一个选项是使用QTimer
)但问题是您是创建一个模型并从另一个线程中添加作为 GUI 一部分的信息,这是 Qt 所禁止的,如docs所示。
考虑到上面最好通过信号将辅助线程的信息发送给主线程,我还实现了一个重置模型信息的方法,避免了创建新模型的需要,最后我添加了验证码所以代码不会失败。
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()
推荐阅读
- c# - IndexOf 用于 char 数组忽略大小写
- typescript - TS7053。隐式具有“any”类型,因为“string”类型的表达式不能用于索引类型“ZoneI”
- vuejs2 - 如何从 b-modal 中的表中获取 ID 并在 VUEJS 中的页面上显示
- javascript - 大图像精灵无法在 Chrome 上正确加载
- c# - Blazor,自定义身份验证,然后基于授权策略
- c - 如何使用 c 语言将我们的内容写在表格中。就像如何在带有行和列的表格中显示我们的内容
- c - 将内存和存储分配到指针中
- sql - SQL 使用记录变量
- javascript - 初学者需要帮助!倒数计时器不起作用JS
- json - 如何对具有多个签名的 JSON 对象进行签名?