When using Qt Designer to make a GUI I am getting inconsistent results between what is previewed in designer versus when it's actually used in code. The actual size of the widgets doesn't change but applying grid layout (or any layout) to a tab widget causes anything inside of the tab widget to be stretched out and not evenly padded as originally shown in Qt Designer.
Preview in Qt Designer (left) and preview when actually initialized in python (right):
Here's the .ui file generated in Qt Designer:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.frame = QtWidgets.QFrame(self.centralwidget)
self.frame.setGeometry(QtCore.QRect(10, 10, 231, 201))
self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame.setObjectName("frame")
self.gridLayout_4 = QtWidgets.QGridLayout(self.frame)
self.gridLayout_4.setObjectName("gridLayout_4")
self.groupBox = QtWidgets.QGroupBox(self.frame)
self.groupBox.setObjectName("groupBox")
self.gridLayout_3 = QtWidgets.QGridLayout(self.groupBox)
self.gridLayout_3.setObjectName("gridLayout_3")
self.tabWidget = QtWidgets.QTabWidget(self.groupBox)
self.tabWidget.setObjectName("tabWidget")
self.tab = QtWidgets.QWidget()
self.tab.setObjectName("tab")
self.gridLayout_2 = QtWidgets.QGridLayout(self.tab)
self.gridLayout_2.setContentsMargins(0, 0, 0, 0)
self.gridLayout_2.setObjectName("gridLayout_2")
self.widget = QtWidgets.QWidget(self.tab)
self.widget.setObjectName("widget")
self.gridLayout_5 = QtWidgets.QGridLayout(self.widget)
self.gridLayout_5.setContentsMargins(0, 0, 0, 0)
self.gridLayout_5.setObjectName("gridLayout_5")
self.groupBox_2 = QtWidgets.QGroupBox(self.widget)
self.groupBox_2.setAutoFillBackground(True)
self.groupBox_2.setTitle("")
self.groupBox_2.setObjectName("groupBox_2")
self.gridLayout = QtWidgets.QGridLayout(self.groupBox_2)
self.gridLayout.setObjectName("gridLayout")
self.pushButton = QtWidgets.QPushButton(self.groupBox_2)
self.pushButton.setObjectName("pushButton")
self.gridLayout.addWidget(self.pushButton, 0, 0, 1, 1)
self.gridLayout_5.addWidget(self.groupBox_2, 0, 0, 1, 1)
self.gridLayout_2.addWidget(self.widget, 0, 0, 1, 1)
self.tabWidget.addTab(self.tab, "")
self.tab_2 = QtWidgets.QWidget()
self.tab_2.setObjectName("tab_2")
self.tabWidget.addTab(self.tab_2, "")
self.gridLayout_3.addWidget(self.tabWidget, 0, 0, 1, 1)
self.gridLayout_4.addWidget(self.groupBox, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.groupBox.setTitle(_translate("MainWindow", "GroupBox"))
self.pushButton.setText(_translate("MainWindow", "PushButton"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "Tab 1"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "Tab 2"))
And here is the code used to initialize the gui:
from PyQt5.QtWidgets import QMainWindow, QApplication
import sys
from ui import Ui_MainWindow
class App(QMainWindow, Ui_MainWindow):
def __init__(self, parent: object = None) -> object:
super(App, self).__init__(parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
if __name__ == "__main__":
app = QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())
You can also verify this yourself by creating a MainWindow > centralwidget > frame > groupbox > tabWidget > groupbox all with grid layout or some kind of layout applied.
I've also tried playing around a bunch with the sizing options for the widgets and the results are still the same. I really hope I don't have to resort to adding spacers all around the inside widget as that would be tedious and result in inconsistent spacing.
Any suggestions would be greatly appreciated as this is very frustrating and surprising it hasn't been addressed before. Thanks!
Qt Designer automatically applies its default margins and spacings to layouts both when designing and previewing, even if they are not set in the "Form Settings..." dialog ("Form" menu). If you go to that dialog and check the "Layout Default" option, and then save the ui file again, you'll probably see the same result in both Designer and your program.
When designing GUIs in Designer you have to remember that every OS and style has different default "metrics", including layout margins and spacings. If you want specific values for every layout you'll need to manually set them in the layout properties, otherwise you can set default values for all the form layouts (and sub-layouts) using the aforementioned option, and then ensure that every layout has the default value set (not marked in "bold").
There's also another possibility: in PyQt5 it is possible to use QProxyStyle (which unfortunately wasn't available on PyQt4), allowing you to override specific methods that are used to draw and "organize" widgets. If you create a subclass of QProxyStyle you can override pixelMetric()
and automatically set margins and spacings for the whole program, as long as you apply it to the main QApplication.
class MyStyle(QtWidgets.QProxyStyle):
defaultMargin = 4
defaultSpacing = 2
margins = QtWidgets.QStyle.PM_LayoutLeftMargin, QtWidgets.QStyle.PM_LayoutTopMargin, QtWidgets.QStyle.PM_LayoutRightMargin, QtWidgets.QStyle.PM_LayoutBottomMargin
spacings = QtWidgets.QStyle.PM_LayoutHorizontalSpacing, QtWidgets.QStyle.PM_LayoutVerticalSpacing
def pixelMetric(self, metric, option=None, widget=None):
if metric in self.margins:
return self.defaultMargin
elif metric in self.spacings:
return self.defaultSpacing
return QtWidgets.QProxyStyle.pixelMetric(self, metric, option, widget)
if __name__ == '__main__':
app = QtWidges.QApplication(sys.argv)
app.setStyle(MyStyle())
[...]