Posts: 211
Threads: 4
Joined: May 2019
I am basically a newbie to PyQt5 but a long time experienced software engineer I have in the last couple of weeks been able to figure out quite a few things about PyQt5 but this one troubles me since I will eventually have numerous columns and I am having to set this information individually for each column using a separate "container" which seems counter-intuitive. This is what I have thus far -- what I am trying to figure out is how to have a single HdrItem and apply it to each of the HeaderLabels -- this gets added into a CenterPane = QSplitter(Qt.Horizontal, self) once its created and that works fine but having to do this for each and every column header seems rather cumbersome which is why it seems counter-intuitive to how PyQt5 works. Note if I do not do it this way it complains about adding duplicates and only the first column header gets the formatting
class ItemDsplyr(QTreeView):
def __init__(self, parent):
QTreeView.__init__(self, parent)
HdrItem0 = QStandardItem()
HdrItem1 = QStandardItem()
HdrItem2 = QStandardItem()
font = QFont()
font.setBold(True)
font.setPointSize(10)
HdrItem0.setFont(font)
HdrItem1.setFont(font)
HdrItem2.setFont(font)
brush = QBrush()
brush.setColor(Qt.blue)
brush.setStyle(Qt.SolidPattern)
HdrItem0.setBackground(brush)
HdrItem0.setForeground(brush)
HdrItem1.setForeground(brush)
HdrItem2.setForeground(brush)
self.model = QStandardItemModel(0, 3)
self.model.setHorizontalHeaderItem(0, HdrItem0)
self.model.setHorizontalHeaderItem(1, HdrItem1)
self.model.setHorizontalHeaderItem(2, HdrItem2)
self.model.setHorizontalHeaderLabels(['Column1', 'Column2', 'Column3'])
self.setModel(self.model)
self.clicked.connect(self.itemSingleClicked)
Posts: 606
Threads: 3
Joined: Nov 2016
We could better help, if you create a small program
with your problem. Then we can run it on our own PCs
for better understanding.
Posts: 211
Threads: 4
Joined: May 2019
May-29-2019, 05:26 PM
(This post was last modified: May-29-2019, 05:26 PM by Denni.)
Quick Container Program for the above Class that runs using Python3.7 and PyQt5
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class ItemDsplyr(QTreeView):
def __init__(self):
QTreeView.__init__(self)
HdrItem0 = QStandardItem()
HdrItem1 = QStandardItem()
HdrItem2 = QStandardItem()
font = QFont()
font.setBold(True)
font.setPointSize(10)
HdrItem0.setFont(font)
HdrItem1.setFont(font)
HdrItem2.setFont(font)
brush = QBrush()
brush.setColor(Qt.blue)
brush.setStyle(Qt.SolidPattern)
HdrItem0.setBackground(brush)
HdrItem0.setForeground(brush)
HdrItem1.setForeground(brush)
HdrItem2.setForeground(brush)
self.model = QStandardItemModel(0, 3)
self.model.setHorizontalHeaderItem(0, HdrItem0)
self.model.setHorizontalHeaderItem(1, HdrItem1)
self.model.setHorizontalHeaderItem(2, HdrItem2)
self.model.setHorizontalHeaderLabels(['Column1', 'Column2', 'Column3'])
self.setModel(self.model)
self.clicked.connect(self.itemSingleClicked)
def itemSingleClicked(self, index):
Item = self.selectedIndexes()[0]
ItemVal = Item.model().itemFromIndex(index).text()
print("Item Clicked:",ItemVal)
def SetContent(self):
self.model.setRowCount(0)
ItmRecSet = [
{'CatgryName':'Cat-1', 'GroupName':'Run-Grp-1', 'ItemName':'Run-Itm-1'},
{'CatgryName':'Cat-1', 'GroupName':'Run-Grp-1', 'ItemName':'Run-Itm-2'},
{'CatgryName':'Cat-1', 'GroupName':'Run-Grp-1', 'ItemName':'Run-Itm-3'}
]
for Item in ItmRecSet:
ItmNam = QStandardItem(Item['ItemName'])
CatNam = QStandardItem(Item['CatgryName'])
GrpNam = QStandardItem(Item['GroupName'])
self.model.appendRow([CatNam, GrpNam, ItmNam])
class CenterPane(QWidget):
def __init__(self, parent):
QWidget.__init__(self)
self.MainWin = parent
self.ItemDsply = ItemDsplyr()
self.ItemDsply.SetContent()
CntrPane = QSplitter(Qt.Horizontal, self)
CntrPane.addWidget(QTextEdit())
CntrPane.addWidget(self.ItemDsply)
CntrPane.setSizes([75,200])
hbox = QHBoxLayout(self)
hbox.addWidget(CntrPane)
self.setLayout(hbox)
@property
def MainWin(self):
return self.__parent
@MainWin.setter
def MainWin(self, value):
self.__parent = value
class Window(QMainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.setCentralWidget(CenterPane(self))
if __name__ == "__main__":
qApp = QApplication([])
GUI = Window()
GUI.show()
sys.exit(qApp.exec_())
Posts: 606
Threads: 3
Joined: Nov 2016
May-30-2019, 07:27 AM
(This post was last modified: May-30-2019, 07:27 AM by heiner55.)
You could use a loop:
self.model = QStandardItemModel(0, 3)
for i in range(3):
HdrItem = QStandardItem()
font = QFont()
font.setBold(True)
font.setPointSize(10)
HdrItem.setFont(font)
brush = QBrush()
brush.setColor(Qt.blue)
brush.setStyle(Qt.SolidPattern)
HdrItem.setBackground(brush)
HdrItem.setForeground(brush)
self.model.setHorizontalHeaderItem(i, HdrItem)
self.model.setHorizontalHeaderLabels(['Column1', 'Column2', 'Column3'])
self.setModel(self.model)
Posts: 211
Threads: 4
Joined: May 2019
Okay but (assuming your suggestion works) that still means I am creating new object for each column which seems a bit excessive so far I have had someone suggest overriding the QStandardItemModel class which would probably be cleaner but it too seems a bit excessive to manipulate that specific bit of information in a cleaner manner. I feel that some where in that vast amount of documentation there is a better way but it like the proverbial needle in a haystack. If for some reason that aspect of the object is truly not accessible like it should be then perhaps the best solution is to (for me) do the override but then submit a solution to the source code as well. Manipulating the formatting of Headers should not be this complicated.
Posts: 8
Threads: 1
Joined: Jun 2019
Jun-04-2019, 05:14 PM
(This post was last modified: Jun-04-2019, 05:14 PM by mfitzp.)
Hey Denni, I think overridding your model is right way to go here: what you're looking to do is change the presentation of your data, which is exactly what the model in model-view is for. This avoids creating needless objects, and allows you to easily update the styles on the fly.
In the example below the column styles are stored in two dictionaries, but you can of course change them out for anything you like.
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
brush = QBrush()
brush.setColor(Qt.blue)
brush.setStyle(Qt.SolidPattern)
COLUMN_FOREGROUND_STYLES = {
1: brush,
2: brush,
3: brush,
}
COLUMN_BACKGROUND_STYLES = {
0: brush,
}
class CustomItemModel(QStandardItemModel):
def headerData(self, section, orientation, role):
if role == Qt.ForegroundRole:
return COLUMN_FOREGROUND_STYLES.get(section)
elif role == Qt.BackgroundRole:
return COLUMN_BACKGROUND_STYLES.get(section)
elif role == Qt.FontRole:
font = QFont()
font.setBold(True)
font.setPointSize(10)
return font
return super().headerData(section, orientation, role)
class ItemDsplyr(QTreeView):
def __init__(self):
QTreeView.__init__(self)
self.model = CustomItemModel(0, 3)
self.model.setHorizontalHeaderLabels(['Column1', 'Column2', 'Column3'])
self.setModel(self.model)
self.clicked.connect(self.itemSingleClicked)
def itemSingleClicked(self, index):
Item = self.selectedIndexes()[0]
ItemVal = Item.model().itemFromIndex(index).text()
print("Item Clicked:",ItemVal)
def SetContent(self):
self.model.setRowCount(0)
ItmRecSet = [
{'CatgryName':'Cat-1', 'GroupName':'Run-Grp-1', 'ItemName':'Run-Itm-1'},
{'CatgryName':'Cat-1', 'GroupName':'Run-Grp-1', 'ItemName':'Run-Itm-2'},
{'CatgryName':'Cat-1', 'GroupName':'Run-Grp-1', 'ItemName':'Run-Itm-3'}
]
for Item in ItmRecSet:
ItmNam = QStandardItem(Item['ItemName'])
CatNam = QStandardItem(Item['CatgryName'])
GrpNam = QStandardItem(Item['GroupName'])
self.model.appendRow([CatNam, GrpNam, ItmNam])
class CenterPane(QWidget):
def __init__(self, parent):
QWidget.__init__(self)
self.MainWin = parent
self.ItemDsply = ItemDsplyr()
self.ItemDsply.SetContent()
CntrPane = QSplitter(Qt.Horizontal, self)
CntrPane.addWidget(QTextEdit())
CntrPane.addWidget(self.ItemDsply)
CntrPane.setSizes([75,200])
hbox = QHBoxLayout(self)
hbox.addWidget(CntrPane)
self.setLayout(hbox)
@property
def MainWin(self):
return self.__parent
@MainWin.setter
def MainWin(self, value):
self.__parent = value
class Window(QMainWindow):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.setCentralWidget(CenterPane(self))
if __name__ == "__main__":
qApp = QApplication([])
GUI = Window()
GUI.show()
sys.exit(qApp.exec_()) Let me know if this is way off the mark.
Posts: 211
Threads: 4
Joined: May 2019
Jun-04-2019, 08:16 PM
(This post was last modified: Jun-04-2019, 08:16 PM by Denni.)
Thanks @mfitzp I think you are right on changing the QStandardItemModel -- seems (at least to me) like modifying that information ought to have been a feature of the standard model but it is looking like it is not. However you outline did give me an idea for how to perhaps streamline that a bit so I changed your CustomItemModel class to look as follows:
class CustomItemModel(QStandardItemModel):
def headerData(self, section, orientation, role):
if role == Qt.ForegroundRole:
brush = QBrush()
brush.setColor(Qt.blue)
brush.setStyle(Qt.SolidPattern)
return brush
elif role == Qt.BackgroundRole:
brush = QBrush()
brush.setColor(Qt.yellow)
brush.setStyle(Qt.SolidPattern)
return brush
elif role == Qt.FontRole:
font = QFont()
font.setBold(True)
font.setPointSize(10)
return font
return super().headerData(section, orientation, role) This gets rid of the multiple instances -- however -- it does not seem to get the Background to work any more than my version did not work -- aka I cannot get the header row to have a different background color even with your version of this. Do you have any idea how one adjusts the background color since this is not working?
Posts: 8
Threads: 1
Joined: Jun 2019
Jun-04-2019, 08:49 PM
(This post was last modified: Jun-04-2019, 08:49 PM by mfitzp.)
Here's a screenshot of your app running on my computer (MacOS)
...and of mine...
Googling a bit I read that on Windows this may not be possible as the header background is overridden by the window style. You can set the application style to Fusion (a cross-platform, Qt-specific style, where everything should work) to at least see if this is the problem.
if __name__ == "__main__":
qApp = QApplication([])
qApp.setStyle("fusion")
GUI = Window()
GUI.show()
sys.exit(qApp.exec_()) If that works then I guess we at least have our answer!
Posts: 211
Threads: 4
Joined: May 2019
Awesome sauce thanks @mfitzp that was the issue with the background looks good now.
|