Jeremy
Jeremy

Reputation: 1

Control position of widgets added after window shows

I am having some trouble with the creation of a GUI using PyQt5.

The final goal is to have a main window from which I can open a sub-window. In the sub-window, I need to be able to create a certain number of couples of line edits.

The position of the line edits must be aligned to 2 upper labels.

I started from what was suggested in this link (PyQT: Add widget after window show). My code was modelled on the answer:

from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication, QMainWindow
import sys

from Window import Ui_MainWindow
from Subwindow import Ui_Subwindow

class Def_Win(QMainWindow):
    def __init__(self):
        super(Def_Win, self).__init__()
        self.ui = Ui_Subwindow()
        self.ui.setupUi(self)
        self.ui.Button_1.clicked.connect(self.AddField)
        self.num = 1
        self.layout = QtWidgets.QVBoxLayout()
        self.layout.addWidget(self.ui.Button_1)

      
    def AddField(self,result):
        Text_reg = QtWidgets.QLineEdit("Text 1 of {}".format(self.num))
        Text_reg.setGeometry(20, 20 + self.num*20, 30, 16)
        Text_reg_2 = QtWidgets.QLineEdit("Text 2 of {}".format(self.num))
        Text_reg_2.setGeometry(40, 20 + self.num*20, 30, 16)
        self.layout.addWidget(Text_reg)
        self.layout.addWidget(Text_reg_2)
        self.num += 1
        if self.num == 10:
            self.ui.Button_1.setEnabled(False)
        
        
def MyWin():
    app = QApplication(sys.argv)
    win = Def_Win()  
    win.show()
    sys.exit(app.exec_())
    
MyWin()

I can get the sub-window (which is already defined in another file). But I can't seem to create the new line edits. In particular, the original code had:

self.layout = QtWidgets.QVBoxLayout(self.ui.centralwidget)

I had to remove the content of the parentheses because the line edits would pile up one after the other, while I would like to have two columns. By doing so, I can no longer create any new line edit.

Here I attach the Subwindow.py code:

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Subwindow(object):
    def setupUi(self, Subwindow):
        Subwindow.setObjectName("Subwindow")
        Subwindow.resize(483, 600)
        self.centralwidget = QtWidgets.QWidget(Subwindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label_1 = QtWidgets.QLabel(self.centralwidget)
        self.label_1.setGeometry(QtCore.QRect(30, 20, 171, 21))
        font = QtGui.QFont()
        font.setFamily("Segoe UI")
        font.setPointSize(12)
        font.setBold(False)
        font.setWeight(50)
        self.label_1.setFont(font)
        self.label_1.setAlignment(QtCore.Qt.AlignCenter)
        self.label_1.setObjectName("label_1")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(280, 20, 171, 21))
        font = QtGui.QFont()
        font.setFamily("Segoe UI")
        font.setPointSize(12)
        self.label_2.setFont(font)
        self.label_2.setAlignment(QtCore.Qt.AlignCenter)
        self.label_2.setObjectName("label_2")
        self.Button_1 = QtWidgets.QPushButton(self.centralwidget)
        self.Button_1.setGeometry(QtCore.QRect(20, 530, 191, 23))
        self.Button_1.setObjectName("Button_1")
        self.Button_2 = QtWidgets.QPushButton(self.centralwidget)
        self.Button_2.setGeometry(QtCore.QRect(380, 530, 75, 23))
        self.Button_2.setObjectName("Button_2")
        Subwindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(Subwindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 483, 21))
        self.menubar.setObjectName("menubar")
        Subwindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(Subwindow)
        self.statusbar.setObjectName("statusbar")
        Subwindow.setStatusBar(self.statusbar)

        self.retranslateUi(Subwindow)
        QtCore.QMetaObject.connectSlotsByName(Subwindow)

    def retranslateUi(self, Subwindow):
        _translate = QtCore.QCoreApplication.translate
        Subwindow.setWindowTitle(_translate("Subwindow", "MainWindow"))
        self.label_1.setText(_translate("Subwindow", "Text 1"))
        self.label_2.setText(_translate("Subwindow", "Text 2"))
        self.Button_1.setText(_translate("Subwindow", "Add text edit"))
        self.Button_2.setText(_translate("Subwindow", "Process"))


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Subwindow = QtWidgets.QMainWindow()
    ui = Ui_Subwindow()
    ui.setupUi(Subwindow)
    Subwindow.show()
    sys.exit(app.exec_())

Upvotes: 0

Views: 98

Answers (1)

musicamante
musicamante

Reputation: 48489

Unfortunately the user who gave that answer has the terrible habit of just giving the final code without explaining the reason of the original issue nor clarifying the modifications, and this usually leads to people randomly trying to replicate that code without understanding what they do and why.

The main problem with what you want to achieve is that you're not using a layout manager, which allows to properly set the geometries of widgets according to a specified organization (in that answer the user created the layout, but, as said, unfortunately didn't explain how it was done and how).

The best solution is to add the layout directly in Designer (here is a more comprehensive guide).

For simple cases, a grid layout for the whole interface could suffice, but since you're going to create widgets dynamically, it's better to rely on nested structures:

  • select both the labels on top, right click on one of them and select "Lay Out Horizontally" from the "Lay out" sub menu;
  • do the same for the buttons on the bottom;
  • add a "Widget" from the widget box, right in the middle;
  • right click on an empty area of the window, and select "Lay Out Vertically" from the layout menu;
  • add an arbitrary widget (for example, a button) to the empty widget in the middle, right click on an empty space of that empty widget and select again a vertical layout, then remove the button (this is required as Designer doesn't allow setting layouts on empty widgets);
  • select the empty widget from the object inspector, scroll down to the bottom of the property editor and rename the layout (the field "layoutName") with a verbose name, for example, buttonsLayout;
  • then, in the code, you can add fields to that layout, using an horizontal layout as container:
    def AddField(self,result):
        Text_reg = QtWidgets.QLineEdit("Text 1 of {}".format(self.num))
        Text_reg.setGeometry(20, 20 + self.num*20, 30, 16)
        Text_reg_2 = QtWidgets.QLineEdit("Text 2 of {}".format(self.num))
        Text_reg_2.setGeometry(40, 20 + self.num*20, 30, 16)
        rowLayout = QtWidgets.QHBoxLayout()
        rowLayout.addWidget(Text_reg)
        rowLayout.addWidget(Text_reg_2)
        self.ui.buttonsLayout.addLayout(rowLayout)
        self.num += 1
        if self.num == 10:
            self.ui.Button_1.setEnabled(False)

A slightly different alternative is to use a QGridLayout for the "container", but in my experience it offers more drawbacks than benefits when dealing with dynamic layouts, as its management of row/column cells is sometimes erratic, and it doesn't allow "inserting" rows or columns in the existing layout.

A couple of suggestions: instead of creating the self.ui object, you can just inherit from both QMainWindow and the ui, using class Def_Win(QMainWindow, Ui_Subwindow):, removing the creation of self.ui and just calling self.setupUi(self). Also, function and variable names should always start with a lower case letter, as only classes and constants should be capitalized.

Upvotes: 1

Related Questions