繁体   English   中英

给PyQt5中的QAbstractTableModel指定行号的特定表行的背景色

[英]Background color a specific table row given row number for QAbstractTableModel in PyQt5

在PyQt5中,我使用模型视图来显示表格。 该模型是QAbstractTableModel,我想将背景色设为第0行。着色有效,但是所有行都变为有色,而不是我指定的行。 另外,当我更改为Qt.Background角色时,我的单元格中出现了一些不需要的“复选框”。 我猜想,这是我对QAbstractTableModel的def数据部分中实际发生的事情的理解,导致我无法达到预期的效果。

正如我已经尝试过的,这是我的代码片段部分。 请注意,在我的情况下,状态1和状态2实际上是True或False。 如果为True,则应将整个行的背景色设为绿色,否则应保持白色。

#Make some dummy data

tabledata = list()
tabledata.append(('item 1', 'amount 1', 'price 1', 'status 1'))
tabledata.append(('item 2', 'amount 2', 'price 2', 'status 2'))

#The self.model below is QAbstractTableModel subclassed

self.model.modelTableData = tabledata

#try set data for just one cell
self.model.setData(self.model.index(0,0), QtCore.Qt.BackgroundRole)
self.model.layoutChanged.emit()

然后在我的QAbstractTableModel类中,我在def数据中具有以下内容

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, parent, header, tabledata):
        #inhert from QAbstractTableModel
        QtCore.QAbstractTableModel.__init__(self, parent)
        self.modelTableData = tabledata
        self.header = header

    def rowCount(self, parent):
        return len(self.modelTableData)

    def columnCount(self, parent):
        return len(self.header)

    def data(self, index, role):
        if not index.isValid():
            return None

        if role == QtCore.Qt.BackgroundRole:
            print('Qt.BackgroundRole at ' + str(index.row()))
            return QtCore.QVariant(QtGui.QColor(QtCore.Qt.green))

        print('Not Qt.BackgroundRole at ' + str(index.row()))
        return self.modelTableData[index.row()][index.column()]

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

我已经搜索了类似的示例并特别研究了这个示例https://python-forum.io/Thread-Change-color-of-a-row-of-a-QTableView

他们似乎要做的是

if role == QtCore.Qt.BackgroundRole and "something more":
    #then do something

我不知道该如何解析为def数据方法。 理想情况下,应该是我的行数据状态1可以为True或False,但是我的理解是def数据部分实际上正在为查看器返回数据吗?

另外,我在代码中对此感到困惑,当我执行打印时,即使我在数据中声明仅将QModelIndex(0,0)的一个单元格设置为绿色,但下一行也设置为绿色。 这种行为的原因是什么?

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'main_v00.ui'
#
# Created by: PyQt5 UI code generator 5.9.2
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets
import POSTools as tool
import json 

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1680, 1050)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.cb_OrderList = QtWidgets.QComboBox(self.centralwidget)
        self.cb_OrderList.setGeometry(QtCore.QRect(160, 30, 111, 31))
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(2)
        sizePolicy.setHeightForWidth(self.cb_OrderList.sizePolicy().hasHeightForWidth())
        self.cb_OrderList.setSizePolicy(sizePolicy)
        self.cb_OrderList.setObjectName("cb_OrderList")
        self.le_NewTable = QtWidgets.QLineEdit(self.centralwidget)
        self.le_NewTable.setGeometry(QtCore.QRect(30, 30, 113, 35))
        self.le_NewTable.setObjectName("le_NewTable")
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(30, 10, 60, 16))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(14)
        self.label.setFont(font)
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(170, 10, 60, 16))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(14)
        self.label_2.setFont(font)
        self.label_2.setObjectName("label_2")
        self.le_ItemName = QtWidgets.QLineEdit(self.centralwidget)
        self.le_ItemName.setGeometry(QtCore.QRect(30, 100, 171, 35))
        self.le_ItemName.setObjectName("le_ItemName")


        self.le_ItemAmount = QtWidgets.QLineEdit(self.centralwidget)
        self.le_ItemAmount.setGeometry(QtCore.QRect(220, 100, 113, 35))
        self.le_ItemAmount.setObjectName("le_ItemAmount")
        self.le_UnitPrice = QtWidgets.QLineEdit(self.centralwidget)
        self.le_UnitPrice.setGeometry(QtCore.QRect(350, 100, 113, 35))
        self.le_UnitPrice.setObjectName("le_UnitPrice")
        self.label_3 = QtWidgets.QLabel(self.centralwidget)
        self.label_3.setGeometry(QtCore.QRect(30, 70, 101, 21))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(14)
        self.label_3.setFont(font)
        self.label_3.setObjectName("label_3")
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(220, 70, 101, 21))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(14)
        self.label_4.setFont(font)
        self.label_4.setObjectName("label_4")
        self.label_5 = QtWidgets.QLabel(self.centralwidget)
        self.label_5.setGeometry(QtCore.QRect(350, 70, 101, 21))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(14)
        self.label_5.setFont(font)
        self.label_5.setObjectName("label_5")
        self.label_6 = QtWidgets.QLabel(self.centralwidget)
        self.label_6.setGeometry(QtCore.QRect(480, 70, 101, 21))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(14)
        self.label_6.setFont(font)
        self.label_6.setObjectName("label_6")
        self.tableView = QtWidgets.QTableView(self.centralwidget)
        self.tableView.setGeometry(QtCore.QRect(30, 150, 711, 461))
        self.tableView.setObjectName("tableView")
        self.pb_remove = QtWidgets.QPushButton(self.centralwidget)
        self.pb_remove.setGeometry(QtCore.QRect(750, 250, 151, 101))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(18)
        self.pb_remove.setFont(font)
        self.pb_remove.setObjectName("pb_remove")
        self.pb_receipt = QtWidgets.QPushButton(self.centralwidget)
        self.pb_receipt.setGeometry(QtCore.QRect(590, 620, 151, 101))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(18)
        self.pb_receipt.setFont(font)
        self.pb_receipt.setObjectName("pb_receipt")
        self.label_TotalPrice = QtWidgets.QLabel(self.centralwidget)
        self.label_TotalPrice.setGeometry(QtCore.QRect(480, 100, 121, 35))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(18)
        self.label_TotalPrice.setFont(font)
        self.label_TotalPrice.setObjectName("label_TotalPrice")
        self.le_Discount = QtWidgets.QLineEdit(self.centralwidget)
        self.le_Discount.setGeometry(QtCore.QRect(610, 100, 131, 35))
        self.le_Discount.setObjectName("le_Discount")
        self.label_7 = QtWidgets.QLabel(self.centralwidget)
        self.label_7.setGeometry(QtCore.QRect(610, 70, 101, 21))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(14)
        self.label_7.setFont(font)
        self.label_7.setObjectName("label_7")
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(750, 150, 151, 101))
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(18)
        self.pushButton.setFont(font)
        self.pushButton.setObjectName("pushButton")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 1680, 22))
        self.menubar.setObjectName("menubar")
        self.menuMore_Options = QtWidgets.QMenu(self.menubar)
        self.menuMore_Options.setObjectName("menuMore_Options")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)
        self.actionCount_Up = QtWidgets.QAction(MainWindow)
        self.actionCount_Up.setObjectName("actionCount_Up")
        self.menuMore_Options.addSeparator()
        self.menuMore_Options.addAction(self.actionCount_Up)
        self.menubar.addAction(self.menuMore_Options.menuAction())

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        MainWindow.setTabOrder(self.le_NewTable, self.cb_OrderList)
        MainWindow.setTabOrder(self.cb_OrderList, self.le_ItemName)
        MainWindow.setTabOrder(self.le_ItemName, self.le_ItemAmount)
        MainWindow.setTabOrder(self.le_ItemAmount, self.le_UnitPrice)
        MainWindow.setTabOrder(self.le_UnitPrice, self.le_Discount)
        MainWindow.setTabOrder(self.le_Discount, self.pushButton)
        MainWindow.setTabOrder(self.pushButton, self.pb_remove)
        MainWindow.setTabOrder(self.pb_remove, self.pb_receipt)
        MainWindow.setTabOrder(self.pb_receipt, self.tableView)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label.setText(_translate("MainWindow", "Order"))
        self.label_2.setText(_translate("MainWindow", "Order List"))
        self.label_3.setText(_translate("MainWindow", "Item Name"))
        self.label_4.setText(_translate("MainWindow", "Item Amount"))
        self.label_5.setText(_translate("MainWindow", "Unit Price"))
        self.label_6.setText(_translate("MainWindow", "Price"))
        self.pb_remove.setText(_translate("MainWindow", "Remove"))
        self.pb_receipt.setText(_translate("MainWindow", "Receipt"))
        self.label_TotalPrice.setText(_translate("MainWindow", "0"))
        self.label_7.setText(_translate("MainWindow", "Discount [%]"))
        self.pushButton.setText(_translate("MainWindow", "Print Kitchen"))
        self.menuMore_Options.setTitle(_translate("MainWindow", "More Options"))
        self.actionCount_Up.setText(_translate("MainWindow", "Count Up"))

class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainWindow,self).__init__()
        self.setupUi(self)
        #Start by reading in the menu items
        self.URL_MenuDB = "somewhere"
        self.path_OrderDB = "somewhere"
        header, menudb = tool.load_MenuDB(self.URL_MenuDB)
        self.MenuHeader = header

        #Prepare the completer by first creating the model
        self.completermodel = QtGui.QStandardItemModel()
        for item in menudb:
            row = list()
            for col in item:
                cell = QtGui.QStandardItem(str(col))
                row.append(cell)
            self.completermodel.appendRow(row)

        self.completer = QtWidgets.QCompleter()
        self.completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
        self.completer.setCompletionColumn(0)
        self.completer.setModel(self.completermodel)
        self.completer.setFilterMode(QtCore.Qt.MatchContains)
        self.completer.activated[QtCore.QModelIndex].connect(self.onActivated)
        self.le_ItemName.setCompleter(self.completer)

        #Setup model view
        self.TableViewHeader = ['Item', 'Qty', 'Price', 'Print status']
        self.TableData = list()
        self.model = TableModel(self, header = self.TableViewHeader, tabledata = self.TableData)
        self.model.layoutChanged.emit()
        self.tableView.setModel(self.model)

        #Upon program starts up, check if OrderDB.txt exists
        status, AllTables = tool.importOrderDB(self.path_OrderDB)
        if status is True: #OrderDB.txt exists
            #check if there is incomplete orders
            self.AllTables = AllTables
            #A list of all active tables
            tablenames = tool.getincompleteOrder(AllTables)
            if tablenames:
                #update the list of tablenames into drop down list
                self.cb_OrderList.clear()
                self.cb_OrderList.addItems(tablenames)
                #get the index of the current active tablename
                self.cb_OrderList.currentText()

        #Define what happens when the user press enter og return for item amount
        self.le_ItemAmount.returnPressed.connect(self.ItemAmountEnterKeyPress)

        #If enter is pressed at unit price, it also connects with self.ItemAmountEnterKeyPress
        self.le_UnitPrice.returnPressed.connect(self.ItemAmountEnterKeyPress)

        #Define what happens when input table edit field is activated
        self.le_NewTable.returnPressed.connect(self.input_newTable)

    def input_newTable(self): #When the user create a new order
        if not self.le_NewTable.text():
            return
        else: 
            #check if OrderDB already exists, if not one will be created. If exists is True, AllTables will be returned
            status, AllTables, tablename, nameclash = tool.ExistOrderDB(self.path_OrderDB, self.le_NewTable.text().strip())

            if nameclash is True:
                tool.msgbox(self,'Bord navn eksisterer. Valg et nyt!')
                self.le_NewTable.clear()
                return

            if status is False: #OrderDB.txt has just been created, and AllTables containing the tableName is returned
                self.AllTables = AllTables
                #Sort all the incomplete tables from All Tables and return the sorted tablename as pandas DataFrame
                tablename = tool.getincompleteOrder(AllTables)
                #insert the tablename as list to drop down list
                self.cb_OrderList.clear()
                self.cb_OrderList.addItems(tablename)
                self.le_NewTable.clear()

            else: #OrderDB.txt exists, continue to create the new table
                #create the tabledict
                tabledict = tool.CreateTableDict(self.le_NewTable.text())
                self.AllTables.append(tabledict)
                #save to data base
                tool.saveOrderDB(self.path_OrderDB, self.AllTables)

                tablename = tabledict["Name"]
                #get a list of all incomplete order names
                ordernames = tool.getincompleteOrder(self.AllTables)
                self.cb_OrderList.clear()
                self.cb_OrderList.addItems(ordernames)
                index = self.cb_OrderList.findText(tablename, QtCore.Qt.MatchFixedString)
                #set the drop down list to the current active index
                self.cb_OrderList.setCurrentIndex(index)

            #Set focus to item field
            self.le_ItemName.setFocus()
            self.le_NewTable.clear()

    def ItemAmountEnterKeyPress(self): #insert the item into the table and update the data base behind the scene
        if not self.cb_OrderList.currentText():
            tool.msgbox(self, 'Select an order first')
            return
        else:
            #Update the selected item into the AllTable
            #Do a match to see if self.selected matches the fields
            inputtext = self.le_ItemName.text()
            if inputtext.strip() == self.selected[0]:
                #the selected is the same as what appears in the field. Check the remaining fields
                qty = tool.isfloat(self.le_ItemAmount.text())
                price = tool.isfloat(self.le_UnitPrice.text())
                if qty is not False and price is not False:
                    #submit the fields to be input into the modelview
                    index = tool.getTableDict(self)

                    #Do the visualization
                    price = [item for item in self.AllTables[index]["orderPrice"]]

                    totalprice = sum(price)
                    self.TableData = list(
                        zip(self.AllTables[index]['itemName'],
                        self.AllTables[index]['orderQty'],
                        price,
                        self.AllTables[index]['PrintStatus_send2kitchen']
                    ))
                    #Update into the model
                    tabledata = list()
                    tabledata.append(('item 1', 'amount 1', 'price 1', 'status 1'))
                    tabledata.append(('item 2', 'amount 2', 'price 2', 'status 2'))

                    self.model.modelTableData = tabledata
                    #self.model.modelTableData = self.TableData

                    #try set data for just one cell
                    self.model.setData(self.model.index(0,0), 
                    QtCore.Qt.BackgroundRole)

                    self.model.layoutChanged.emit()

                    #tool.plotTable(self, totalprice)

                    #clear
                    self.clearInputFields()
                    self.le_ItemName.setFocus()

                else: #the item in the fields is different from self.selected, ask the user to try again
                    tool.msgbox(self, 'Item er ikke korrekt valgt, prøv igen.')
                    self.clearInputFields(self)
                    self.le_ItemName.setFocus()
                    return

            else: #User has not selected from the list
                tool.msgbox(self,'Prøv igen. Du skal vælge fra listen når du indsætter item')
                self.clearInputFields(self)
                self.le_ItemName.setFocus()
                return

    def clearInputFields(self):
        self.le_ItemName.clear()
        self.le_ItemAmount.clear()
        self.le_UnitPrice.clear()

    @QtCore.pyqtSlot(QtCore.QModelIndex)
    def onActivated(self, index):
        self.selected = list()
        for i in range(0, len(self.MenuHeader) +1):
            self.selected.append(index.sibling(index.row(),i).data())

        #display the selected item in the editfield
        self.le_UnitPrice.setText(self.selected[3])

        #change focus to amount
        self.le_ItemAmount.setFocus()

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, parent, header, tabledata):
        #inhert from QAbstractTableModel
        QtCore.QAbstractTableModel.__init__(self, parent)
        self.modelTableData = tabledata
        self.header = header

    def rowCount(self, parent):
        return len(self.modelTableData)

    def columnCount(self, parent):
        return len(self.header)

    def data(self, index, role):
        if not index.isValid():
            return None

        if role == QtCore.Qt.BackgroundRole:
            return QtCore.QVariant(QtGui.QColor(QtCore.Qt.green))

        return self.modelTableData[index.row()][index.column()]

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

if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())


预期的结果是仅对指定的行进行着色。 此外,我假设与Qt.Background相关的每个单元格上的刻度线也被删除。

谢谢!

data()方法的逻辑是过滤信息,在这种情况下,我将使用字典,同时考虑到您希望绘制完整的行,然后保存与所选行关联但与列0关联的QPersistentModel,最后是该方法setData()默认情况下不执行任何操作,因此您必须重写。

指示该复选框的错误是因为您正在返回其他角色(例如Qt :: CheckStateRole)的文本,相反,您必须按照我已经指出的那样过滤信息

from PyQt5 import QtCore, QtGui, QtWidgets


class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, parent, header, tabledata):
        QtCore.QAbstractTableModel.__init__(self, parent)
        self.modelTableData = tabledata
        self.header = header

        self.background_colors = dict()

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self.modelTableData)

    def columnCount(self, parent=QtCore.QModelIndex()):
        return len(self.header)

    def data(self, index, role):
        if not index.isValid():
            return None
        if (
            0 <= index.row() < self.rowCount()
            and 0 <= index.column() < self.columnCount()
        ):
            if role == QtCore.Qt.BackgroundRole:
                ix = self.index(index.row(), 0)
                pix = QtCore.QPersistentModelIndex(ix)
                if pix in self.background_colors:
                    color = self.background_colors[pix]
                    return color
            elif role == QtCore.Qt.DisplayRole:
                return self.modelTableData[index.row()][index.column()]

    def setData(self, index, value, role):
        if not index.isValid():
            return False
        if (
            0 <= index.row() < self.rowCount()
            and 0 <= index.column() < self.columnCount()
        ):
            if role == QtCore.Qt.BackgroundRole and index.isValid():
                ix = self.index(index.row(), 0)
                pix = QtCore.QPersistentModelIndex(ix)
                self.background_colors[pix] = value
                return True
        return False

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


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = QtWidgets.QTableView()
    header = ["Item", "Qty", "Price", "Print status"]

    tabledata = [
        ("item 1", "amount 1", "price 1", "status 1"),
        ("item 2", "amount 2", "price 2", "status 2"),
    ]
    model = TableModel(None, header, tabledata)
    model.setData(
        model.index(0, 0), QtGui.QColor(QtCore.Qt.green), QtCore.Qt.BackgroundRole
    )
    w.setModel(model)
    w.show()
    sys.exit(app.exec_())

@eyllanesc非常感谢您给我另一个学习的机会。 意识到我需要过滤信息,这对我们有所帮助。 另外,我意识到我实际上不需要setData即可工作。 以下是我根据您的评论和示例重写代码的方式。 我只剩下一个问题,我在下面实现def数据的方式,这不会使代码变得非常昂贵,在某种意义上说,需要执行枚举器循环来识别每次def数据需要着色的行叫做?

class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, parent, header, tabledata):
        #inhert from QAbstractTableModel
        QtCore.QAbstractTableModel.__init__(self, parent)
        self.modelTableData = tabledata
        self.header = header

    def rowCount(self, parent):
        return len(self.modelTableData)

    def columnCount(self, parent):
        return len(self.header)

    def data(self, index, role):
        if not index.isValid():
            return None

        if role == QtCore.Qt.CheckStateRole:
            return None

        #get rows to be colored
        rowscolor = list()
        for item in enumerate(self.modelTableData):
            if item[1][-1] == False:
                rowscolor.append(item[0])

        if role == QtCore.Qt.BackgroundRole and index.row() in rowscolor:
            return QtCore.QVariant(QtGui.QColor(QtCore.Qt.green))

        return self.modelTableData[index.row()][index.column()]

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM