Using Python 3.7 with pyqt5 on Win10
I am working on getting a QTreeView that uses a QStandardItemModel to display more than 10 lines. I want it to show the maximum number of lines the area can actually handle but it stops short at 10 for some unknown (by me at least) reason.
I have tried numerous solutions (some I have remarked out within the code below along with their apparent results) and there are others that I am not including because they did not compile. As of yet I cannot seem to find a way to get the QTreeView to fill the available space and/or show all the lines it can it just stops at 10 lines. I am including a program that runs and shows the issue simply click "Options" then click "Option 1"
from sys import exit as sysExit
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Line(QFrame):
def __init__(self, parent):
QFrame.__init__(self)
self.setFrameShape(QFrame.HLine)
self.setFrameShadow(QFrame.Sunken)
# Users Widget *************************************************
class Disply1(QFrame):
def __init__(self, parent):
QFrame.__init__(self)
self.setFrameShape(QFrame.StyledPanel)
self.setLineWidth(0.2)
# -------
self.header = QVBoxLayout()
Title = QLabel()
Title.setStyleSheet('font: bold 14px')
Title.setText('Option 1 Configuration Settings')
self.header.addWidget(Title)
self.header.addWidget(Line(self))
# -------
self.TreeVew = QTreeView()
self.TreeVew.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.TreeVew.setAlternatingRowColors(True)
# This adjustment did not fix it
# self.TreeVew.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
# This adjustment made it worse
# self.TreeVew.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
self.model = QStandardItemModel(0, 6)
self.model.setHorizontalHeaderLabels([' ', 'Column1', 'Column2', 'Column3', 'Column4', 'Column5'])
self.model.setHeaderData(5, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole)
# This adjustment did not fix it
# self.model.setRowCount(20)
self.TreeVew.setModel(self.model)
self.TreeVew.setColumnWidth(0, 1)
self.TreeVew.setColumnHidden(0, True)
self.TreeVew.header().setSectionResizeMode(0, QHeaderView.Fixed)
self.TreeVew.header().setStretchLastSection(False)
self.TreeVew.header().setSectionResizeMode(1, QHeaderView.Stretch)
self.TreeVew.setColumnWidth(2, 100)
self.TreeVew.setColumnWidth(3, 100)
self.TreeVew.setColumnWidth(4, 100)
self.TreeVew.setColumnWidth(5, 70)
# This adjustment did not fix it
# self.TreeVew.resize(self.sizeHint().width(), self.maximumHeight())
ItmRecSet = [
{'Id':'01', 'Column1':'R01-C1', 'Column2':'R01-C2', 'Column3':'R01-C3', 'Column4':'R01-C5', 'Column5':'R01-C5'},
{'Id':'02', 'Column1':'R02-C1', 'Column2':'R02-C2', 'Column3':'R02-C3', 'Column4':'R02-C5', 'Column5':'R02-C5'},
{'Id':'03', 'Column1':'R03-C1', 'Column2':'R03-C2', 'Column3':'R03-C3', 'Column4':'R03-C5', 'Column5':'R03-C5'},
{'Id':'04', 'Column1':'R04-C1', 'Column2':'R04-C2', 'Column3':'R04-C3', 'Column4':'R04-C5', 'Column5':'R04-C5'},
{'Id':'05', 'Column1':'R05-C1', 'Column2':'R05-C2', 'Column3':'R05-C3', 'Column4':'R05-C5', 'Column5':'R05-C5'},
{'Id':'06', 'Column1':'R06-C1', 'Column2':'R06-C2', 'Column3':'R06-C3', 'Column4':'R06-C5', 'Column5':'R06-C5'},
{'Id':'07', 'Column1':'R07-C1', 'Column2':'R07-C2', 'Column3':'R07-C3', 'Column4':'R07-C5', 'Column5':'R07-C5'},
{'Id':'08', 'Column1':'R08-C1', 'Column2':'R08-C2', 'Column3':'R08-C3', 'Column4':'R08-C5', 'Column5':'R08-C5'},
{'Id':'09', 'Column1':'R09-C1', 'Column2':'R09-C2', 'Column3':'R09-C3', 'Column4':'R09-C5', 'Column5':'R09-C5'},
{'Id':'10', 'Column1':'R10-C1', 'Column2':'R10-C2', 'Column3':'R10-C3', 'Column4':'R10-C5', 'Column5':'R10-C5'},
{'Id':'11', 'Column1':'R11-C1', 'Column2':'R11-C2', 'Column3':'R11-C3', 'Column4':'R11-C5', 'Column5':'R11-C5'},
{'Id':'12', 'Column1':'R12-C1', 'Column2':'R12-C2', 'Column3':'R12-C3', 'Column4':'R12-C5', 'Column5':'R12-C5'},
{'Id':'13', 'Column1':'R13-C1', 'Column2':'R13-C2', 'Column3':'R13-C3', 'Column4':'R13-C5', 'Column5':'R13-C5'},
{'Id':'14', 'Column1':'R14-C1', 'Column2':'R14-C2', 'Column3':'R14-C3', 'Column4':'R14-C5', 'Column5':'R14-C5'},
{'Id':'15', 'Column1':'R15-C1', 'Column2':'R15-C2', 'Column3':'R15-C3', 'Column4':'R15-C5', 'Column5':'R15-C5'},
{'Id':'16', 'Column1':'R16-C1', 'Column2':'R16-C2', 'Column3':'R16-C3', 'Column4':'R16-C5', 'Column5':'R16-C5'},
{'Id':'17', 'Column1':'R17-C1', 'Column2':'R17-C2', 'Column3':'R17-C3', 'Column4':'R17-C5', 'Column5':'R17-C5'},
{'Id':'18', 'Column1':'R18-C1', 'Column2':'R18-C2', 'Column3':'R18-C3', 'Column4':'R18-C5', 'Column5':'R18-C5'},
{'Id':'19', 'Column1':'R19-C1', 'Column2':'R19-C2', 'Column3':'R19-C3', 'Column4':'R19-C5', 'Column5':'R19-C5'},
{'Id':'20', 'Column1':'R20-C1', 'Column2':'R20-C2', 'Column3':'R20-C3', 'Column4':'R20-C5', 'Column5':'R20-C5'}
]
self.model.setRowCount(0)
for Item in ItmRecSet:
Itm1 = QStandardItem(Item['Id'])
Itm1.setTextAlignment(Qt.AlignLeft)
Itm1.isEditable = False
Itm2 = QStandardItem(Item['Column1'])
Itm2.setTextAlignment(Qt.AlignLeft)
Itm2.isEditable = False
Itm3 = QStandardItem(Item['Column2'])
Itm3.setTextAlignment(Qt.AlignLeft)
Itm3.isEditable = False
Itm4 = QStandardItem(Item['Column3'])
Itm4.setTextAlignment(Qt.AlignLeft)
Itm4.isEditable = False
Itm5 = QStandardItem(Item['Column4'])
Itm5.setTextAlignment(Qt.AlignLeft)
Itm5.isEditable = False
Itm6 = QStandardItem(Item['Column5'])
Itm6.setTextAlignment(Qt.AlignCenter)
Itm6.isEditable = False
self.model.appendRow([Itm1, Itm2, Itm3, Itm4, Itm5, Itm6])
# Using this GridLayout versus the VerticalBox did not fix it
# self.Cntnr = QGridLayout()
# -------
# self.Cntnr.addLayout(self.header,0,0)
# self.Cntnr.addWidget(self.TreeVew,1,0)
# self.Cntnr.setRowStretch(2,4)
self.Cntnr = QVBoxLayout()
self.Cntnr.addLayout(self.header)
self.Cntnr.addWidget(self.TreeVew)
# If this stretch is not included only the header gets stretched the QTreeView still only shows 10 lines
self.Cntnr.addStretch(1)
# -------
self.setLayout(self.Cntnr)
# Debug Widget *************************************************
class Disply2(QFrame):
def __init__(self, parent):
QFrame.__init__(self)
self.setFrameShape(QFrame.StyledPanel)
self.setLineWidth(0.2)
# -------
self.Cntnr = QVBoxLayout()
self.Cntnr.addWidget(QTextEdit('Option 2 Settings Shown Here'))
self.Cntnr.addStretch(1)
# -------
self.setLayout(self.Cntnr)
class OptionButtons(QToolButton):
# Class OptionButtons ("Text", Icon, Connector) inherits from QToolButton
def __init__(self, Text, Icon, Connector):
QToolButton.__init__(self)
self.setText(Text)
self.setIcon(Icon)
self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
self.setStyleSheet("font: bold;color: blue;height: 55px;width: 55px;")
self.setIconSize(QSize(32,32))
self.clicked.connect(Connector)
############################## Settings Class ##############################
class OptionSettings(QDialog):
def __init__(self, parent):
QDialog.__init__(self, parent)
line = QFrame()
line.setFrameShape(QFrame.HLine)
line.setFrameShadow(QFrame.Sunken)
# Button Area on Left
# Button 1 *************************************************
self.btnOptn1 = OptionButtons('Option1', QIcon('images/opt1.ico'), self.ShowDisply1)
# Button 2 *************************************************
self.btnOptn2 = OptionButtons('Option2', QIcon('images/opt2.ico'), self.ShowDisply2)
# Vertical Box for Buttons *************************************
self.UpLeft = QVBoxLayout()
self.UpLeft.addWidget(self.btnOptn1)
self.UpLeft.addWidget(self.btnOptn2)
self.UpLeft.addStretch(1)
# Display Area on Right
# Widget Flip Display ******************************************
self.UpRite = QHBoxLayout()
self.Contents = QStackedWidget()
self.Contents.addWidget(QTextEdit('Nothing Selected'))
self.Contents.addWidget(Disply1(self))
self.Contents.addWidget(Disply2(self))
self.Contents.addWidget(QTextEdit('Settings Saved'))
self.Contents.setCurrentIndex(0)
self.UpRite.addWidget(self.Contents)
# Button and Display Area on Top
self.Upper = QHBoxLayout()
self.Upper.addLayout(self.UpLeft)
self.Upper.addLayout(self.UpRite)
# Save and Cancel Area on Bottom
self.btnSave = QPushButton("Save")
self.btnSave.clicked.connect(self.SaveSettings)
self.btnCncl = QPushButton("Cancel")
self.btnCncl.clicked.connect(self.close)
self.Lower = QHBoxLayout()
self.Lower.addStretch(1)
self.Lower.addWidget(self.btnSave)
self.Lower.addWidget(self.btnCncl)
# Entire Options Window Layout
self.OuterBox = QVBoxLayout()
self.OuterBox.addLayout(self.Upper)
self.OuterBox.addLayout(self.Lower)
self.setLayout(self.OuterBox)
self.setWindowTitle('Settings')
#Geometry(Left, Top, Width, Hight)
self.setGeometry(250, 250, 550, 450)
self.setModal(True)
self.exec()
def ShowDisply1(self):
self.Contents.setCurrentIndex(1)
def ShowDisply2(self):
self.Contents.setCurrentIndex(2)
def SaveSettings(self):
self.Contents.setCurrentIndex(3)
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)
class ItemDsplyr(QTreeView):
def __init__(self, CentrPane):
QTreeView.__init__(self, CentrPane)
self.CntrPane = CentrPane
self.setEditTriggers(QTreeView().NoEditTriggers)
self.model = CustomItemModel(0, 3)
self.model.setHorizontalHeaderLabels(['1st Col', '2nd Col', '3rd Col'])
self.model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole)
self.setModel(self.model)
self.setMinimumWidth(250)
self.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
self.header().setStretchLastSection(False)
self.header().setSectionResizeMode(0, QHeaderView.Stretch)
self.setColumnWidth(1, 75)
self.setColumnWidth(2, 100)
class CenterPanel(QWidget):
def __init__(self, MainWin):
QWidget.__init__(self)
self.MyEditor = QTextEdit('Editorial')
self.ItemDsply = ItemDsplyr(self)
CntrPane = QSplitter(Qt.Horizontal, self)
CntrPane.addWidget(self.MyEditor)
CntrPane.addWidget(self.ItemDsply)
CntrPane.setSizes([50,200])
CntrPane.setCollapsible(0, False)
CntrPane.setCollapsible(1, False)
hbox = QHBoxLayout(self)
hbox.addWidget(CntrPane)
self.setLayout(hbox)
class MenuToolBar(QDockWidget):
def __init__(self, MainWin):
QDockWidget.__init__(self)
self.MainWin = MainWin
self.MainMenu = MainWin.menuBar()
self.WndowMenu = self.MainMenu.addMenu('Windows')
self.OptnAct = QAction('Options', self)
self.OptnAct.setStatusTip('Open the Options Window')
self.OptnAct.triggered.connect(MainWin.ShowOptions)
self.WndowMenu.addAction(self.OptnAct)
self.InitToolBar(MainWin)
def InitToolBar(self, MainWin):
self.mainToolBar = MainWin.addToolBar("Quick Access")
self.mainToolBar.addAction(self.OptnAct)
class UI_MainWindow(QMainWindow):
def __init__(self, MainDesktop):
super(UI_MainWindow, self).__init__(MainDesktop)
self.setWindowTitle('Main Window')
self.MnDskTop = MainDesktop
# Left, Top, Width, Height
self.setGeometry(200, 200, 550, 550)
self.CenterPane = CenterPanel(self)
self.setCentralWidget(self.CenterPane)
self.MenuToolBar = MenuToolBar(self)
def ShowOptions(self):
self.Options = OptionSettings(self)
if __name__ == '__main__':
MainApp = QApplication([])
MainGui = UI_MainWindow(MainApp.desktop())
MainGui.show()
sysExit(MainApp.exec_())
Again what I had expected is that the QTreeView would fill the available space and/or show as many rows as possible within the given space. However, as stated it stops short and only displays just 10 lines maximum.
I removed the line self.Cntnr.addStretch(1), that's what happened:
from sys import exit as sysExit
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Line(QFrame):
def __init__(self, parent):
QFrame.__init__(self)
self.setFrameShape(QFrame.HLine)
self.setFrameShadow(QFrame.Sunken)
# Users Widget *************************************************
class Disply1(QFrame):
def __init__(self, parent):
QFrame.__init__(self)
self.setFrameShape(QFrame.StyledPanel)
self.setLineWidth(0.2)
# -------
self.header = QVBoxLayout()
Title = QLabel()
Title.setStyleSheet('font: bold 14px')
Title.setText('Option 1 Configuration Settings')
self.header.addWidget(Title)
self.header.addWidget(Line(self))
# -------
self.TreeVew = QTreeView()
self.TreeVew.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.TreeVew.setAlternatingRowColors(True)
# This adjustment did not fix it
# self.TreeVew.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum)
# This adjustment made it worse
# self.TreeVew.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
self.model = QStandardItemModel(0, 6)
self.model.setHorizontalHeaderLabels([' ', 'Column1', 'Column2', 'Column3', 'Column4', 'Column5'])
self.model.setHeaderData(5, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole)
# This adjustment did not fix it
# self.model.setRowCount(20)
self.TreeVew.setModel(self.model)
self.TreeVew.setColumnWidth(0, 1)
self.TreeVew.setColumnHidden(0, True)
self.TreeVew.header().setSectionResizeMode(0, QHeaderView.Fixed)
self.TreeVew.header().setStretchLastSection(False)
self.TreeVew.header().setSectionResizeMode(1, QHeaderView.Stretch)
self.TreeVew.setColumnWidth(2, 100)
self.TreeVew.setColumnWidth(3, 100)
self.TreeVew.setColumnWidth(4, 100)
self.TreeVew.setColumnWidth(5, 70)
# This adjustment did not fix it
# self.TreeVew.resize(self.sizeHint().width(), self.maximumHeight())
## self.TreeVew.resize(400, 600)
ItmRecSet = [
{'Id':'01', 'Column1':'R01-C1', 'Column2':'R01-C2', 'Column3':'R01-C3', 'Column4':'R01-C5', 'Column5':'R01-C5'},
{'Id':'02', 'Column1':'R02-C1', 'Column2':'R02-C2', 'Column3':'R02-C3', 'Column4':'R02-C5', 'Column5':'R02-C5'},
{'Id':'03', 'Column1':'R03-C1', 'Column2':'R03-C2', 'Column3':'R03-C3', 'Column4':'R03-C5', 'Column5':'R03-C5'},
{'Id':'04', 'Column1':'R04-C1', 'Column2':'R04-C2', 'Column3':'R04-C3', 'Column4':'R04-C5', 'Column5':'R04-C5'},
{'Id':'05', 'Column1':'R05-C1', 'Column2':'R05-C2', 'Column3':'R05-C3', 'Column4':'R05-C5', 'Column5':'R05-C5'},
{'Id':'06', 'Column1':'R06-C1', 'Column2':'R06-C2', 'Column3':'R06-C3', 'Column4':'R06-C5', 'Column5':'R06-C5'},
{'Id':'07', 'Column1':'R07-C1', 'Column2':'R07-C2', 'Column3':'R07-C3', 'Column4':'R07-C5', 'Column5':'R07-C5'},
{'Id':'08', 'Column1':'R08-C1', 'Column2':'R08-C2', 'Column3':'R08-C3', 'Column4':'R08-C5', 'Column5':'R08-C5'},
{'Id':'09', 'Column1':'R09-C1', 'Column2':'R09-C2', 'Column3':'R09-C3', 'Column4':'R09-C5', 'Column5':'R09-C5'},
{'Id':'10', 'Column1':'R10-C1', 'Column2':'R10-C2', 'Column3':'R10-C3', 'Column4':'R10-C5', 'Column5':'R10-C5'},
{'Id':'11', 'Column1':'R11-C1', 'Column2':'R11-C2', 'Column3':'R11-C3', 'Column4':'R11-C5', 'Column5':'R11-C5'},
{'Id':'12', 'Column1':'R12-C1', 'Column2':'R12-C2', 'Column3':'R12-C3', 'Column4':'R12-C5', 'Column5':'R12-C5'},
{'Id':'13', 'Column1':'R13-C1', 'Column2':'R13-C2', 'Column3':'R13-C3', 'Column4':'R13-C5', 'Column5':'R13-C5'},
{'Id':'14', 'Column1':'R14-C1', 'Column2':'R14-C2', 'Column3':'R14-C3', 'Column4':'R14-C5', 'Column5':'R14-C5'},
{'Id':'15', 'Column1':'R15-C1', 'Column2':'R15-C2', 'Column3':'R15-C3', 'Column4':'R15-C5', 'Column5':'R15-C5'},
{'Id':'16', 'Column1':'R16-C1', 'Column2':'R16-C2', 'Column3':'R16-C3', 'Column4':'R16-C5', 'Column5':'R16-C5'},
{'Id':'17', 'Column1':'R17-C1', 'Column2':'R17-C2', 'Column3':'R17-C3', 'Column4':'R17-C5', 'Column5':'R17-C5'},
{'Id':'18', 'Column1':'R18-C1', 'Column2':'R18-C2', 'Column3':'R18-C3', 'Column4':'R18-C5', 'Column5':'R18-C5'},
{'Id':'19', 'Column1':'R19-C1', 'Column2':'R19-C2', 'Column3':'R19-C3', 'Column4':'R19-C5', 'Column5':'R19-C5'},
{'Id':'20', 'Column1':'R20-C1', 'Column2':'R20-C2', 'Column3':'R20-C3', 'Column4':'R20-C5', 'Column5':'R20-C5'}
]
self.model.setRowCount(0)
for Item in ItmRecSet:
Itm1 = QStandardItem(Item['Id'])
Itm1.setTextAlignment(Qt.AlignLeft)
Itm1.isEditable = False
Itm2 = QStandardItem(Item['Column1'])
Itm2.setTextAlignment(Qt.AlignLeft)
Itm2.isEditable = False
Itm3 = QStandardItem(Item['Column2'])
Itm3.setTextAlignment(Qt.AlignLeft)
Itm3.isEditable = False
Itm4 = QStandardItem(Item['Column3'])
Itm4.setTextAlignment(Qt.AlignLeft)
Itm4.isEditable = False
Itm5 = QStandardItem(Item['Column4'])
Itm5.setTextAlignment(Qt.AlignLeft)
Itm5.isEditable = False
Itm6 = QStandardItem(Item['Column5'])
Itm6.setTextAlignment(Qt.AlignCenter)
Itm6.isEditable = False
self.model.appendRow([Itm1, Itm2, Itm3, Itm4, Itm5, Itm6])
# Using this GridLayout versus the VerticalBox did not fix it
# self.Cntnr = QGridLayout()
# -------
# self.Cntnr.addLayout(self.header,0,0)
# self.Cntnr.addWidget(self.TreeVew,1,0)
# self.Cntnr.setRowStretch(2,4)
self.Cntnr = QVBoxLayout()
self.Cntnr.addLayout(self.header)
self.Cntnr.addWidget(self.TreeVew)
# If this stretch is not included only the header gets stretched the QTreeView still only shows 10 lines
#--- self.Cntnr.addStretch(1) # <<<=======
# -------
self.setLayout(self.Cntnr)
# Debug Widget *************************************************
class Disply2(QFrame):
def __init__(self, parent):
QFrame.__init__(self)
self.setFrameShape(QFrame.StyledPanel)
self.setLineWidth(0.2)
# -------
self.Cntnr = QVBoxLayout()
self.Cntnr.addWidget(QTextEdit('Option 2 Settings Shown Here'))
self.Cntnr.addStretch(1)
# -------
self.setLayout(self.Cntnr)
class OptionButtons(QToolButton):
# Class OptionButtons ("Text", Icon, Connector) inherits from QToolButton
def __init__(self, Text, Icon, Connector):
QToolButton.__init__(self)
self.setText(Text)
self.setIcon(Icon)
self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
self.setStyleSheet("font: bold;color: blue;height: 55px;width: 55px;")
self.setIconSize(QSize(32,32))
self.clicked.connect(Connector)
############################## Settings Class ##############################
class OptionSettings(QDialog):
def __init__(self, parent):
QDialog.__init__(self, parent)
line = QFrame()
line.setFrameShape(QFrame.HLine)
line.setFrameShadow(QFrame.Sunken)
# Button Area on Left
# Button 1 *************************************************
self.btnOptn1 = OptionButtons('Option1', QIcon('Ok.png'), self.ShowDisply1)
# Button 2 *************************************************
self.btnOptn2 = OptionButtons('Option2', QIcon('images/opt2.ico'), self.ShowDisply2)
# Vertical Box for Buttons *************************************
self.UpLeft = QVBoxLayout()
self.UpLeft.addWidget(self.btnOptn1)
self.UpLeft.addWidget(self.btnOptn2)
self.UpLeft.addStretch(1)
# Display Area on Right
# Widget Flip Display ******************************************
self.UpRite = QHBoxLayout()
self.Contents = QStackedWidget()
self.Contents.addWidget(QTextEdit('Nothing Selected'))
self.Contents.addWidget(Disply1(self))
self.Contents.addWidget(Disply2(self))
self.Contents.addWidget(QTextEdit('Settings Saved'))
self.Contents.setCurrentIndex(0)
self.UpRite.addWidget(self.Contents)
# Button and Display Area on Top
self.Upper = QHBoxLayout()
self.Upper.addLayout(self.UpLeft)
self.Upper.addLayout(self.UpRite)
# Save and Cancel Area on Bottom
self.btnSave = QPushButton("Save")
self.btnSave.clicked.connect(self.SaveSettings)
self.btnCncl = QPushButton("Cancel")
self.btnCncl.clicked.connect(self.close)
self.Lower = QHBoxLayout()
self.Lower.addStretch(1)
self.Lower.addWidget(self.btnSave)
self.Lower.addWidget(self.btnCncl)
# Entire Options Window Layout
self.OuterBox = QVBoxLayout()
self.OuterBox.addLayout(self.Upper)
self.OuterBox.addLayout(self.Lower)
self.setLayout(self.OuterBox)
self.setWindowTitle('Settings')
#Geometry(Left, Top, Width, Hight)
self.setGeometry(250, 250, 550, 450)
self.setModal(True)
self.exec()
def ShowDisply1(self):
self.Contents.setCurrentIndex(1)
def ShowDisply2(self):
self.Contents.setCurrentIndex(2)
def SaveSettings(self):
self.Contents.setCurrentIndex(3)
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)
class ItemDsplyr(QTreeView):
def __init__(self, CentrPane):
QTreeView.__init__(self, CentrPane)
self.CntrPane = CentrPane
self.setEditTriggers(QTreeView().NoEditTriggers)
self.model = CustomItemModel(0, 3)
self.model.setHorizontalHeaderLabels(['1st Col', '2nd Col', '3rd Col'])
self.model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole)
self.setModel(self.model)
self.setMinimumWidth(250)
self.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents)
self.header().setStretchLastSection(False)
self.header().setSectionResizeMode(0, QHeaderView.Stretch)
self.setColumnWidth(1, 75)
self.setColumnWidth(2, 100)
class CenterPanel(QWidget):
def __init__(self, MainWin):
QWidget.__init__(self)
self.MyEditor = QTextEdit('Editorial')
self.ItemDsply = ItemDsplyr(self)
CntrPane = QSplitter(Qt.Horizontal, self)
CntrPane.addWidget(self.MyEditor)
CntrPane.addWidget(self.ItemDsply)
CntrPane.setSizes([50,200])
CntrPane.setCollapsible(0, False)
CntrPane.setCollapsible(1, False)
hbox = QHBoxLayout(self)
hbox.addWidget(CntrPane)
self.setLayout(hbox)
class MenuToolBar(QDockWidget):
def __init__(self, MainWin):
QDockWidget.__init__(self)
self.MainWin = MainWin
self.MainMenu = MainWin.menuBar()
self.WndowMenu = self.MainMenu.addMenu('Windows')
self.OptnAct = QAction('Options', self)
self.OptnAct.setStatusTip('Open the Options Window')
self.OptnAct.triggered.connect(MainWin.ShowOptions)
self.WndowMenu.addAction(self.OptnAct)
self.InitToolBar(MainWin)
def InitToolBar(self, MainWin):
self.mainToolBar = MainWin.addToolBar("Quick Access")
self.mainToolBar.addAction(self.OptnAct)
class UI_MainWindow(QMainWindow):
def __init__(self, MainDesktop):
super(UI_MainWindow, self).__init__(MainDesktop)
self.setWindowTitle('Main Window')
self.MnDskTop = MainDesktop
# Left, Top, Width, Height
self.setGeometry(200, 200, 550, 550)
self.CenterPane = CenterPanel(self)
self.setCentralWidget(self.CenterPane)
self.MenuToolBar = MenuToolBar(self)
def ShowOptions(self):
self.Options = OptionSettings(self)
if __name__ == '__main__':
MainApp = QApplication([])
MainGui = UI_MainWindow(MainApp.desktop())
MainGui.show()
sysExit(MainApp.exec_())
Related
My program collecting some data from the user (patient ID, trial no., activity, etc.) using a dialog window with a QFormLayout. I want to save these data and print them onto the main window, but when I print them they come out empty. I'm new to Python so I'm wondering if there's a structural issue with my code that is causing the variables to be lost.
I have a main class for the main window that looks like this (there is more but I cut out irrelevant parts):
class App(QMainWindow):
""" Sets up the main window for the Graphical User Interface """
def __init__(self):
super().__init__()
self.title = 'Title'
self.left = 60
self.top = 100
self.width = 1400
self.height = 820
self.initUI()
def initUI(self) :
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
*lots of labels and buttons added here*
inputs = getInputs()
inputs.exec()
patID = inputs.patID
patAct = inputs.patAct
patTrial = inputs.patTrial
# print out inputs under their labels
label_pat2 = QLabel(str(patID), self)
label_pat2.move(150, 200)
label_activity2 = QLabel(str(patAct), self)
label_activity2.move(300, 200)
label_trial2= QLabel(str(patTrial), self)
label_trial2.move(500, 200)
self.show() # show main window
Then I have another class that opens a dialog window to get the inputs, which looks like this:
class getInputs(QDialog):
""" Takes in the patient ID number, trial number, and patient activity """
# NumGridRows = 3
# NumButtons = 4
def __init__(self):
super().__init__()
# super(getInputs, self).__init__()
self.createFormGroupBox()
buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
mainLayout = QVBoxLayout()
mainLayout.addWidget(self.formGroupBox)
mainLayout.addWidget(buttonBox)
self.setLayout(mainLayout)
self.setWindowTitle("Trial information Input Dialog")
self.resize(400,200)
def createFormGroupBox(self):
combo_box = QComboBox()
combo_box.addItem("Walking")
combo_box.addItem("Running")
combo_box.addItem("Slow Walking")
line_edit = QLineEdit()
spin_box = QSpinBox()
self.formGroupBox = QGroupBox("Form layout")
layout = QFormLayout()
layout.addRow(QLabel("Patient ID:"), line_edit)
layout.addRow(QLabel("Activity:"), combo_box)
layout.addRow(QLabel("Trial no.:"), spin_box)
self.formGroupBox.setLayout(layout)
self.patID = line_edit.text()
self.patAct = combo_box.currentText()
self.patTrial = spin_box.text()
My theories right now are that there is either an error in my structure that causes the results from the window to be deleted, or that the .text() and .currentText() functions might not be the right function to use.
The problem is that you are getting the data an instant after creating the widgets when the user still does not interact with the QDialog. On the other hand, to obtain the value of the QSpinBox you must use the value() method:
class getInputs(QDialog):
"""Takes in the patient ID number, trial number, and patient activity"""
# NumGridRows = 3
# NumButtons = 4
def __init__(self):
super().__init__()
# super(getInputs, self).__init__()
self.createFormGroupBox()
buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
buttonBox.accepted.connect(self.accept)
buttonBox.rejected.connect(self.reject)
mainLayout = QVBoxLayout()
mainLayout.addWidget(self.formGroupBox)
mainLayout.addWidget(buttonBox)
self.setLayout(mainLayout)
self.setWindowTitle("Trial information Input Dialog")
self.resize(400, 200)
def createFormGroupBox(self):
self.combo_box = QComboBox()
self.combo_box.addItem("Walking")
self.combo_box.addItem("Running")
self.combo_box.addItem("Slow Walking")
self.line_edit = QLineEdit()
self.spin_box = QSpinBox()
self.formGroupBox = QGroupBox("Form layout")
layout = QFormLayout()
layout.addRow(QLabel("Patient ID:"), self.line_edit)
layout.addRow(QLabel("Activity:"), self.combo_box)
layout.addRow(QLabel("Trial no.:"), self.spin_box)
self.formGroupBox.setLayout(layout)
def exec(self):
super().exec()
self.patID = self.line_edit.text()
self.patAct = self.combo_box.currentText()
self.patTrial = self.spin_box.value()
I am attempting to design a label class that inherits from the PyQt5 base QLabel class that is able to track another widget. Here is the current code for my class:
class AttachedLabel(QLabel):
def __init__(self, attachedTo, *args, side="left", ** kwargs):
super().__init__(*args, **kwargs) # Run parent initialization
# Define instance variables
self.attached = attachedTo
self.side = side
# Update label position
self.updatePos()
def updatePos(self):
# Get "attached widget" position and dimensions
x = self.attached.geometry().x()
y = self.attached.geometry().y()
aWidth = self.attached.geometry().width()
aHeight = self.attached.geometry().height()
# Get own dimensions
width = self.geometry().width()
height = self.geometry().height()
if self.side == "top": # Above of attached widget
self.setGeometry(x, y-height, width, height)
elif self.side == "bottom": # Below attached widget
self.setGeometry(x, y+height+aHeight, width, height)
elif self.side == "right": # Right of attached widget
self.setGeometry(x + width + aWidth, y, width, height)
else: # Left of attached widget
self.setGeometry(x - width, y, width, height)
I want to be able to instantiate the label like so:
AttachedLabel(self.pushButton, self.centralwidget)
where self.pushButton is the widget it is supposed to be following. The issue is that I don't know how to detect when the widget moves in order to run my updatePos() function. I would ideally only update the label position when the other widget moves, but I want to refrain from havign to add extra code to the class of the widget that is being tracked. I have tried overriding the paintEvent, but that only triggers when the object itself needs to be redrawn, so it doesn't even function as a sub-optimal solution.
Is there some built-in method I can use/override to detect when the widget moves or when the screen itself is updated?
You have to use an eventFilter intersecting the QEvent::Move event and you should also track the resize through the QEvent::Resize event.
from dataclasses import dataclass, field
import random
from PyQt5 import QtCore, QtWidgets
class GeometryTracker(QtCore.QObject):
geometryChanged = QtCore.pyqtSignal()
def __init__(self, widget):
super().__init__(widget)
self._widget = widget
self.widget.installEventFilter(self)
#property
def widget(self):
return self._widget
def eventFilter(self, source, event):
if self.widget is source and event.type() in (
QtCore.QEvent.Move,
QtCore.QEvent.Resize,
):
self.geometryChanged.emit()
return super().eventFilter(source, event)
#dataclass
class TrackerManager:
widget1: field(default_factory=QtWidgets.QWidget)
widget2: field(default_factory=QtWidgets.QWidget)
alignment: QtCore.Qt.Alignment = QtCore.Qt.AlignLeft
enabled: bool = True
valid_alignments = (
QtCore.Qt.AlignLeft,
QtCore.Qt.AlignRight,
QtCore.Qt.AlignHCenter,
QtCore.Qt.AlignTop,
QtCore.Qt.AlignBottom,
QtCore.Qt.AlignVCenter,
)
def __post_init__(self):
self._traker = GeometryTracker(self.widget1)
self._traker.geometryChanged.connect(self.update)
if not any(self.alignment & flag for flag in self.valid_alignments):
raise ValueError("alignment is not valid")
def update(self):
if not self.enabled:
return
r = self.widget1.rect()
p1 = r.center()
c1 = r.center()
if self.alignment & QtCore.Qt.AlignLeft:
p1.setX(r.left())
if self.alignment & QtCore.Qt.AlignRight:
p1.setX(r.right())
if self.alignment & QtCore.Qt.AlignTop:
p1.setY(r.top())
if self.alignment & QtCore.Qt.AlignBottom:
p1.setY(r.bottom())
p2 = self.convert_position(p1)
c2 = self.convert_position(c1)
g = self.widget2.geometry()
g.moveCenter(c2)
if self.alignment & QtCore.Qt.AlignLeft:
g.moveRight(p2.x())
if self.alignment & QtCore.Qt.AlignRight:
g.moveLeft(p2.x())
if self.alignment & QtCore.Qt.AlignTop:
g.moveBottom(p2.y())
if self.alignment & QtCore.Qt.AlignBottom:
g.moveTop(p2.y())
self.widget2.setGeometry(g)
def convert_position(self, point):
gp = self.widget1.mapToGlobal(point)
if self.widget2.isWindow():
return gp
return self.widget2.parent().mapFromGlobal(gp)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.button = QtWidgets.QPushButton("Press me", self)
self.label = QtWidgets.QLabel(
"Tracker\nLabel", self, alignment=QtCore.Qt.AlignCenter
)
self.label.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents, True)
self.label.setFixedSize(200, 200)
self.label.setStyleSheet(
"background-color: salmon; border: 1px solid black; font-size: 40pt;"
)
self.resize(640, 480)
self.manager = TrackerManager(
widget1=self.button,
widget2=self.label,
alignment=QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter,
)
self.move_button()
def move_button(self):
pos = QtCore.QPoint(*random.sample(range(400), 2))
animation = QtCore.QPropertyAnimation(
targetObject=self.button,
parent=self,
propertyName=b"pos",
duration=1000,
startValue=self.button.pos(),
endValue=pos,
)
animation.finished.connect(self.move_button)
animation.start(QtCore.QAbstractAnimation.DeleteWhenStopped)
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
I am using PySide2 and I cant find any documentation on how to use the paint() function in a QStyledItemDelegate subclass. I am rather new to classes but is so far understandable but having trouble with PySide2.
I would like to replace my QtWidgets.QListWidgetItem with my own ListWidgetItem and display them correctly, like this:
So on the left of the ListWidgetItem an icon a bit to the right the name of the ListWidgetItem and underneath the description.
Here is the code:
from PySide2 import QtWidgets, QtCore, QtGui
from PySide2.QtGui import *
import sys
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__()
self.setWindowTitle('Test Window')
self.setStyleSheet("background-color: rgb(65, 65, 65);")
mainWidget = QtWidgets.QWidget(self)
self.setCentralWidget(mainWidget)
self.boxLayout = QtWidgets.QVBoxLayout()
mainWidget.setLayout(self.boxLayout)
# Add Widgets
self.textField = QtWidgets.QLineEdit()
self.listView = QtWidgets.QListWidget()
self.textField.textChanged.connect(self.onTextChanged)
self.boxLayout.addWidget(self.textField)
self.boxLayout.addWidget(self.listView)
self.textField.setFocus()
def onTextChanged(self, ):
titles = ['Monkey', 'Giraffe', 'Dragon', 'Bull']
descriptions = ['Almost a homo sapiens sapiens', 'I am a Giraffe!', 'Can fly and is hot on spices', 'Horny...']
if self.textField.text() == '' or self.textField.text().isspace() or self.textField.text() == ' ':
if self.listView.count() > 0:
self.listView.clear()
else:
if self.listView.count() > 0:
self.listView.clear()
for x in range(len(titles)):
if self.textField.text() in titles[x]:
item = ListWidgetItem(titles[x])
self.listView.addItem(item)
self.listView.setCurrentRow(0)
continue
class ListWidgetItem(QtWidgets.QListWidgetItem):
def __init__(self, title = '', description = '', icon = QtGui.QIcon()):
super(ListWidgetItem, self).__init__()
self.title = title
self.description = description
self.icon = icon
class ListViewStyle(QtWidgets.QStyledItemDelegate):
def __init__(self, parent, itemModel):
super(ListViewStyle, self).__init__(parent)
self.itemModel = itemModel
def sizeHint(self, option, index):
if index:
return QtCore.QSize(40, 40)
def paint(self, painter, option, index):
super(ListViewStyle, self).paint(painter, option, index)
if __name__ == '__main__':
app = QtWidgets.QApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
#sys.exit(app.exec_())
Info: In onTextChanged() the ListWidgetItem will be added to the QListWidget but not drawn correctly, basically empty.
Does QListWidgetItem have any notable difference to QListView?
The delegate only paints and is not interested in what element provides the information since that class uses the QModelIndex and the same model to obtain the information, so in my previous solution I used a QStandardItemModel that uses QStandardItem and in your current case a QListWidget with QListWidgetItem is indifferent. My delegate expects only that the information of the title, description and icon are related to TitleRole, DescriptionRole and IconRole, respectively.
On the other hand it is not good to delete the items but it is better to hide or make them visible when necessary.
Considering the above, the solution with QListWidget is as follows:
import sys
from PySide2 import QtWidgets, QtCore, QtGui
TitleRole = QtCore.Qt.UserRole + 1000
DescriptionRole = QtCore.Qt.UserRole + 1001
IconRole = QtCore.Qt.UserRole + 1002
class ListWidgetItem(QtWidgets.QListWidgetItem):
def __init__(self, title="", description="", icon=QtGui.QIcon()):
super(ListWidgetItem, self).__init__()
self.title = title
self.description = description
self.icon = icon
#property
def title(self):
return self.data(TitleRole)
#title.setter
def title(self, title):
self.setData(TitleRole, title)
#property
def description(self):
return self.data(DescriptionRole)
#description.setter
def description(self, description):
self.setData(DescriptionRole, description)
#property
def icon(self):
return self.data(IconRole)
#icon.setter
def icon(self, icon):
self.setData(IconRole, icon)
class StyledItemDelegate(QtWidgets.QStyledItemDelegate):
def sizeHint(self, option, index):
return QtCore.QSize(50, 50)
def paint(self, painter, option, index):
super(StyledItemDelegate, self).paint(painter, option, index)
title = index.data(TitleRole)
description = index.data(DescriptionRole)
icon = index.data(IconRole)
mode = QtGui.QIcon.Normal
if not (option.state & QtWidgets.QStyle.State_Enabled):
mode = QtGui.QIcon.Disabled
elif option.state & QtWidgets.QStyle.State_Selected:
mode = QtGui.QIcon.Selected
state = (
QtGui.QIcon.On
if option.state & QtWidgets.QStyle.State_Open
else QtGui.QIcon.Off
)
iconRect = QtCore.QRect(option.rect)
iconRect.setSize(QtCore.QSize(40, 40))
icon.paint(
painter, iconRect, QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, mode, state
)
titleFont = QtGui.QFont(option.font)
titleFont.setPixelSize(20)
fm = QtGui.QFontMetrics(titleFont)
titleRect = QtCore.QRect(option.rect)
titleRect.setLeft(iconRect.right())
titleRect.setHeight(fm.height())
color = (
option.palette.color(QtGui.QPalette.BrightText)
if option.state & QtWidgets.QStyle.State_Selected
else option.palette.color(QtGui.QPalette.WindowText)
)
painter.save()
painter.setFont(titleFont)
pen = painter.pen()
pen.setColor(color)
painter.setPen(pen)
painter.drawText(titleRect, title)
painter.restore()
descriptionFont = QtGui.QFont(option.font)
descriptionFont.setPixelSize(15)
fm = QtGui.QFontMetrics(descriptionFont)
descriptionRect = QtCore.QRect(option.rect)
descriptionRect.setTopLeft(titleRect.bottomLeft())
descriptionRect.setHeight(fm.height())
painter.save()
painter.setFont(descriptionFont)
pen = painter.pen()
pen.setColor(color)
painter.setPen(pen)
painter.drawText(
descriptionRect,
fm.elidedText(description, QtCore.Qt.ElideRight, descriptionRect.width()),
)
painter.restore()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__()
self.setWindowTitle("Test Window")
self.setStyleSheet("background-color: rgb(65, 65, 65);")
mainWidget = QtWidgets.QWidget(self)
self.setCentralWidget(mainWidget)
self.boxLayout = QtWidgets.QVBoxLayout()
mainWidget.setLayout(self.boxLayout)
# Add Widgets
self.textField = QtWidgets.QLineEdit()
self.listView = QtWidgets.QListWidget()
self.textField.textChanged.connect(self.onTextChanged)
self.boxLayout.addWidget(self.textField)
self.boxLayout.addWidget(self.listView)
self.fill_model()
self.textField.setFocus()
self.listView.setItemDelegate(StyledItemDelegate(self))
def fill_model(self):
titles = ["Monkey", "Giraffe", "Dragon", "Bull"]
descriptions = [
"Almost a homo sapiens sapiens",
"I am a Giraffe!",
"Can fly and is hot on spices",
"Horny...",
]
for title, description in zip(titles, descriptions):
it = ListWidgetItem(title=title, description=description)
self.listView.addItem(it)
#QtCore.Slot(str)
def onTextChanged(self, text):
text = text.strip()
if text:
for i in range(self.listView.count()):
it = self.listView.item(i)
if it is not None:
it.setHidden(text.lower() not in it.title.lower())
else:
for i in range(self.listView.count()):
it = self.listView.item(i)
if it is not None:
it.setHidden(False)
if __name__ == "__main__":
app = QtWidgets.QApplication.instance()
if app is None:
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Is there an attribute to position subwindows in qmdiarea? I’m trying to center subwindow in middle of mainwindow on startup (mdiarea)
I’m working on a mcve but haven’t finished it, wanted to see if anyone has tried doing this, and how they did it
Subwindows are randomly placed on startup when initialized
class App(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QMainWindow.__init__(self, parent=parent)
self.setupUi(self)
self.screenShape = QDesktopWidget().screenGeometry()
self.width = self.screenShape.width()
self.height = self.screenShape.height()
self.resize(self.width * .6, self.height * .6)
self.new = []
#calls GUI's in other modules
self.lw = Login()
self.vs = VS()
self.ms = MS()
self.hw = HomeWindow()
self.mw = MainWindow()
self.ga = GA()
self.sGUI = Settings()
# shows subwindow
self.CreateLogin()
self.CreateVS()
self.CreateMS()
self.CreateGA()
self.CreateSettings()
def CreateLogin(self):
self.subwindow = QMdiSubWindow()
self.subwindow.setWidget(self.lw)
self.subwindow.setAttribute(Qt.WA_DeleteOnClose, True)
self.mdiArea.addSubWindow(self.subwindow)
self.subwindow.setMaximumSize(520, 300)
self.subwindow.setMinimumSize(520, 300)
self.lw.showNormal()
def CreateVS(self):
self.subwindow = QMdiSubWindow()
self.subwindow.setWidget(self.vs)
self.mdiArea.addSubWindow(self.subwindow)
self.vs.showMinimized()
def CreateMS(self):
self.subwindow = QMdiSubWindow()
self.subwindow.setWidget(self.ms)
self.mdiArea.addSubWindow(self.subwindow)
self.ms.showMinimized()
self.ms.tabWidget.setCurrentIndex(0)
def CreateGA(self):
self.subwindow = QMdiSubWindow()
self.subwindow.setWidget(self.ga)
self.mdiArea.addSubWindow(self.subwindow)
self.ga.showMinimized()
self.subwindow.setMaximumSize(820, 650)
def CreateSettings(self):
self.subwindow = QMdiSubWindow()
self.subwindow.setWidget(self.sGUI)
self.mdiArea.addSubWindow(self.subwindow)
self.sGUI.showMinimized()
def CreateWindow(self):
self.hw.pushButton.clicked.connect(self.vs.showNormal)
self.hw.pushButton_2.clicked.connect(self.Moduleprogram)
self.hw.pushButton_3.clicked.connect(self.ms.showNormal)
self.hw.pushButton_4.clicked.connect(self.ga.showNormal)
self.subwindow = QMdiSubWindow()
self.subwindow.setWindowFlags(Qt.CustomizeWindowHint | Qt.Tool)
self.subwindow.setWidget(self.hw)
self.subwindow.setMaximumSize(258, 264)
self.subwindow.move(self.newwidth*.35, self.newheight*.25)
self.mdiArea.addSubWindow(self.subwindow)
In Qt the geometry is only effective when the window is visible so if you want to center something it must be in the showEvent method. On the other hand to center the QMdiSubWindow you must first get the center of the viewport of the QMdiArea, and according to that modify the geometry of the QMdiSubWindow.
Because the code you provide is complicated to execute, I have created my own code
from PyQt5 import QtCore, QtGui, QtWidgets
import random
def create_widget():
widget = QtWidgets.QLabel(
str(random.randint(0, 100)), alignment=QtCore.Qt.AlignCenter
)
widget.setStyleSheet(
"""background-color: {};""".format(
QtGui.QColor(*random.sample(range(255), 3)).name()
)
)
widget.setMinimumSize(*random.sample(range(100, 300), 2))
return widget
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
add_button = QtWidgets.QPushButton(
"Add subwindow", clicked=self.add_subwindow
)
self._mdiarea = QtWidgets.QMdiArea()
central_widget = QtWidgets.QWidget()
self.setCentralWidget(central_widget)
lay = QtWidgets.QVBoxLayout(central_widget)
lay.addWidget(add_button)
lay.addWidget(self._mdiarea)
self._is_first_time = True
for _ in range(4):
self.add_subwindow()
#QtCore.pyqtSlot()
def add_subwindow(self):
widget = create_widget()
subwindow = QtWidgets.QMdiSubWindow(self._mdiarea)
subwindow.setWidget(widget)
subwindow.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
subwindow.show()
self._mdiarea.addSubWindow(subwindow)
# self.center_subwindow(subwindow)
def showEvent(self, event):
if self.isVisible() and self._is_first_time:
for subwindow in self._mdiarea.subWindowList():
self.center_subwindow(subwindow)
self._is_first_time = False
def center_subwindow(self, subwindow):
center = self._mdiarea.viewport().rect().center()
geo = subwindow.geometry()
geo.moveCenter(center)
subwindow.setGeometry(geo)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Update:
If you want the subwindow to be centered then with the following code you have to create a property center to True:
def add_subwindow(self):
widget = create_widget()
subwindow = QtWidgets.QMdiSubWindow(self._mdiarea)
subwindow.setWidget(widget)
subwindow.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
subwindow.show()
subwindow.setProperty("center", True) # <----
self._mdiarea.addSubWindow(subwindow)
def showEvent(self, event):
if self.isVisible() and self._is_first_time:
for subwindow in self._mdiarea.subWindowList():
if subwindow.property("center"): # <---
self.center_subwindow(subwindow)
self._is_first_time = False
I have several tabs and inside the "admin" tab I want to display two pages: one locked page (before entering credentials) and another unlocked page (after successful login). To do this, I'm using a QStackedWidget() to switch between the two pages. I have created a locked login screen but can't seem to move the object to the center of the page.
I have looked at moving widgets inside QStackedWidget and centering widgets in the center of the screen but my objects do not seem to change position. I've tried to move the entire internal widget using move() to the center of the screen using the desktop dimension and the parent widget to no avail. How would I be able to move the login fields to the center of the page? Thanks!
Current:
Desired:
Code:
from PyQt4 import QtGui, QtCore
# from load_CSS import load_CSS
# from widgets import UniversalPlotWidget
import sys
import time
def exit_application():
"""Exit program event handler"""
sys.exit(1)
class VerticalTabBar(QtGui.QTabBar):
def __init__(self, width, height, parent=None):
super(VerticalTabBar, self).__init__(parent)
self.width = width
self.height = height
def tabSizeHint(self, index):
return QtCore.QSize(self.width, self.height)
def paintEvent(self, event):
painter = QtGui.QStylePainter(self)
tab_options = QtGui.QStyleOptionTab()
for tab in range(self.count()):
self.initStyleOption(tab_options, tab)
painter.drawControl(QtGui.QStyle.CE_TabBarTabShape, tab_options)
painter.save()
size = tab_options.rect.size()
size.transpose()
rectangle = QtCore.QRect(QtCore.QPoint(), size)
rectangle.moveCenter(tab_options.rect.center())
tab_options.rect = rectangle
center = self.tabRect(tab).center()
painter.translate(center)
painter.rotate(90)
painter.translate(-center)
painter.drawControl(QtGui.QStyle.CE_TabBarTabLabel, tab_options);
painter.restore()
class TabWidget(QtGui.QTabWidget):
def __init__(self, *args, **kwargs):
QtGui.QTabWidget.__init__(self, *args, **kwargs)
self.setTabBar(VerticalTabBar(kwargs.pop('width'), kwargs.pop('height')))
self.setTabPosition(QtGui.QTabWidget.West)
self.setTabShape(QtGui.QTabWidget.Rounded)
class AdminTabWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(AdminTabWidget, self).__init__(parent)
self.setWindowModality(QtCore.Qt.ApplicationModal)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
self.admin_page_locked_init()
self.admin_page_unlocked_init()
self.admin_page_layout = QtGui.QGridLayout()
self.admin_page_switch = QtGui.QStackedWidget()
self.admin_page_switch.addWidget(self.admin_locked_tab)
self.admin_page_switch.addWidget(self.admin_unlocked_tab)
self.admin_page_switch.setCurrentIndex(0)
self.admin_page_layout.addWidget(self.admin_page_switch,0,0)
def admin_page_locked_init(self):
self.admin_locked_tab = QtGui.QWidget()
self.admin_locked_tab.setFixedSize(550,225)
self.admin_locked_layout = QtGui.QGridLayout()
self.username_label = QtGui.QLabel('Username: ')
self.username_field = QtGui.QLineEdit()
self.username_field.returnPressed.connect(self.verify_credentials)
self.space_label = QtGui.QLabel(' ')
self.space_label.setFixedHeight(25)
self.password_label = QtGui.QLabel('Password: ')
self.password_field = QtGui.QLineEdit()
self.password_field.returnPressed.connect(self.verify_credentials)
self.password_field.setEchoMode(QtGui.QLineEdit.Password)
self.verify_button = QtGui.QPushButton('Ok')
self.verify_button.clicked.connect(self.verify_credentials)
self.cancel_button = QtGui.QPushButton('Cancel')
self.cancel_button.clicked.connect(self.unauthorized)
self.status_label = QtGui.QLabel('')
self.status_label.setAlignment(QtCore.Qt.AlignCenter)
self.button_layout = QtGui.QGridLayout()
self.button_layout.addWidget(self.verify_button,0,0,1,1)
self.button_layout.addWidget(self.cancel_button,0,1,1,1)
self.admin_locked_layout.addWidget(self.username_label,0,0,1,1)
self.admin_locked_layout.addWidget(self.username_field,0,1,1,1)
self.admin_locked_layout.addWidget(self.space_label,1,0,1,3)
self.admin_locked_layout.addWidget(self.password_label,2,0,1,1)
self.admin_locked_layout.addWidget(self.password_field,2,1,1,1)
self.admin_locked_layout.addWidget(self.status_label,3,0,1,3)
self.admin_locked_layout.addLayout(self.button_layout,4,0,1,3)
self.admin_locked_tab.setLayout(self.admin_locked_layout)
def verify_credentials(self):
print('button pressed')
# Grab username/password from input fields
self.username = str(self.username_field.text())
self.password = str(self.password_field.text())
self.status_label.setText('Verifying')
self.status_label.setStyleSheet('QLabel {color: rgb(117,255,161)}')
self.spin(.001)
print('verified')
def spin(self, seconds):
"""Pause for set amount of seconds, replaces time.sleep so program doesnt stall"""
time_end = time.time() + seconds
while time.time() < time_end:
QtGui.QApplication.processEvents()
def unauthorized(self):
print('unauthorized')
self.status_label.setText('Invalid username and/or password')
self.status_label.setStyleSheet('QLabel {color: rgb(255,65,106)}')
def admin_page_unlocked_init(self):
self.admin_unlocked_tab = QtGui.QWidget()
admin_unlocked_layout = QtGui.QGridLayout()
admin_unlocked_button = QtGui.QPushButton('unlocked')
admin_unlocked_layout.addWidget(admin_unlocked_button)
self.admin_unlocked_tab.setLayout(admin_unlocked_layout)
def get_admin_page_layout(self):
return self.admin_page_layout
if __name__ == '__main__':
# Create main application window
app = QtGui.QApplication(sys.argv)
# app.setStyleSheet(load_CSS(1))
app.setStyle(QtGui.QStyleFactory.create("Cleanlooks"))
font = QtGui.QFont('Ubuntu', 20)
font.setWeight(70)
app.setFont(font)
screen_height = QtGui.QApplication.desktop().screenGeometry().height()
main_window_tab = TabWidget(width=300, height=screen_height/8)
main_window_tab.setWindowTitle("Tab Layout")
main_window_tab.setWindowFlags(QtCore.Qt.FramelessWindowHint)
main_window_tab.showMaximized()
tab1 = QtGui.QWidget()
tab2 = QtGui.QWidget()
tab3 = QtGui.QWidget()
tab4 = QtGui.QWidget()
tab5 = QtGui.QWidget()
tab6 = QtGui.QWidget()
tab7 = QtGui.QWidget()
admin_tab = QtGui.QWidget()
admin_tab_widget = AdminTabWidget()
admin_tab.setLayout(admin_tab_widget.get_admin_page_layout())
main_window_tab.addTab(admin_tab, "Admin")
main_window_tab.addTab(tab1, "tab1")
main_window_tab.addTab(tab2, "tab2")
main_window_tab.addTab(tab3, "tab3")
main_window_tab.addTab(tab4, "tab4")
main_window_tab.addTab(tab5, "tab5")
main_window_tab.addTab(tab6, "tab6")
main_window_tab.addTab(tab7, "tab7")
main_window_tab.show()
QtGui.QShortcut(QtGui.QKeySequence('Ctrl+Q'), main_window_tab, exit_application)
sys.exit(app.exec_())
The idea is to set the QStackedWidget with the Qt::AlignCenter alignment in the layout so it changes:
self.admin_page_layout.addWidget(self.admin_page_switch, 0, 0)
to:
self.admin_page_layout.addWidget(self.admin_page_switch, 0, 0, alignment=QtCore.Qt.AlignCenter)