Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Dynamic adding of textbox in pyqt5



  • hi, guys,

    i have a question about a gui project. If the user selects one or more images on the gui, the names of the images should be displayed in text boxes. Currently i have implemented 3 textboxes statically. Therefore only the names of 3 images are displayed. Is there a way to make this more dynamic ? For example, if the user selects more than 3, the gui will be extended by two more textboxes ?

    I build the gui with qt designer. In the image, you can see the prototype guiUnbenannt.PNG . And this is the code:

        def __init__(self):
            super(Fenster, self).__init__()
            loadUi(r'C:\Users\NAME\Desktop\xyz\Code_Fr_Forum\test_.ui', self)
    
            self.pushButton.clicked.connect(self.load_img)
    
        def load_img(self):
            filename, _ = QFileDialog.getOpenFileNames(self, 'Select Multi File', 'default', 'All Files (*)')
            count = len(filename)
            #Myidea: Depend on the number of filenames, add more than 3 lineEdit on 
            #the GUI (dynamic). 
            #![alt text](![image url](![image url](image url)))Now its very static
            for index, name in enumerate(filename, start=1):
                # extract file name
                basename = os.path.basename(name)
                filename_list.append(basename)
    
                if index == 1: 
                    self.lineEdit.setText(basename)
    
                elif index == 2               
                    self.lineEdit_1.setText(basename)
    
                elif index == 3 and idx != -1:
                    self.lineEdit_2.setText(basename)
                
               #elif index > 3:
                    #add self.lineEdit_3 under self.lineEdit_2
                         self.lineEdit_3.setText(basename)
    
    
    ######MAIN####################
    app = QApplication(sys.argv)
    w = Fenster()
    w.show()
    sys.exit(app.exec_())```

  • Lifetime Qt Champion

    @elias_hh said in Dynamic adding of textbox in pyqt5:

    Is there a way to make this more dynamic ?

    Of course. You can add a list member variable where you store your line edits.

    for index, name in enumerate(filename, start=1):
        # extract file name
        basename = os.path.basename(name)
        filename_list.append(basename)
        self.lineEdits += QLineEdit(self) # Make sure to set correct parent
        # Use the line edit you just added
    

    Use a layout to organise all this widgets.


  • Banned

    @elias_hh here is a MUC that shows you how to do many things -- as well as explains why for somethings as well -- if you have any questions on it just ask.

    from PyQt5.QtCore    import pyqtSlot
    from PyQt5.QtGui     import QFont, QStandardItemModel, QStandardItem
    from PyQt5.QtWidgets import QApplication, QStyleFactory, QMainWindow, QWidget
    from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout, QLabel, QPushButton
    from PyQt5.QtWidgets import QFileDialog, QLineEdit, QTextEdit
    from PyQt5.QtWidgets import QListView
    
    from os import path as osPath
    
    class OutWithListView(QWidget):
        def __init__(self, parent):
            QWidget.__init__(self)
            self.Parent = parent
            self.lvwImgLst = QListView()
            self.lvwModel = QStandardItemModel()
            self.lvwImgLst.setModel(self.lvwModel)
          # These two are not really need just included to make
          # all the classes interchangeable
            self.PrimrSet = False
            self.LstIndx = 0
            
            VBox = QVBoxLayout()
            VBox.addWidget(self.lvwImgLst)
    
            self.setLayout(VBox)
    
      # Note Indx is used here only to make the various classes
      # interchangeable it is not needed otherwise
        def SetImage(self, Text, Indx):
          # One could add Images to this one such as the file icon
          # Item = QStandardItem(QIcon(SomeIcon), Text)
            Item = QStandardItem(Text)
            self.lvwModel.appendRow(Item)
    
    
    class OutWithTextEdit(QWidget):
        def __init__(self, parent):
            QWidget.__init__(self)
            self.Parent = parent
            self.txeImgLst = QTextEdit()
          # These two are not really need just included to make
          # all the classes interchangeable
            self.PrimrSet = False
            self.LstIndx = 0
            
            VBox = QVBoxLayout()
            VBox.addWidget(self.txeImgLst)
    
            self.setLayout(VBox)
    
      # Note Indx is used here only to make the various classes
      # interchangeable it is not needed otherwise
        def SetImage(self, Text, Indx):
            self.txeImgLst.append(Text)
    
    
    # Note this would most likely need a means to either re-size
    # the window or better yet have a Scroll Area added to it
    class OutWithLabels(QWidget):
        def __init__(self, parent):
            QWidget.__init__(self)
            self.Parent = parent
            self.LblViews = {}
            self.LblViews[0] = QLabel()
            self.PrimrSet = False
            self.LstIndx = 0
            
            self.EditBox = QVBoxLayout()
            self.EditBox.addWidget(self.LblViews[0])
    
            self.setLayout(self.EditBox)
    
        def SetImage(self, Text, Indx):
            if Indx not in self.LblViews.keys():
                self.LblViews[Indx] = QLabel()
                self.EditBox.addWidget(self.LblViews[Indx])
    
            self.LblViews[Indx].setText(Text)
    
    
    # Note this would most likely need a means to either re-size
    # the window or better yet have a Scroll Area added to it
    class OutWithLineEdits(QWidget):
        def __init__(self, parent):
            QWidget.__init__(self)
            self.Parent = parent
            self.LneEdts = {}
            self.LneEdts[0] = QLineEdit()
            self.PrimrSet = False
            self.LstIndx = 0
            
            self.EditBox = QVBoxLayout()
            self.EditBox.addWidget(self.LneEdts[0])
    
            self.setLayout(self.EditBox)
    
        def SetImage(self, Text, Indx):
            print('Setting Image ',Indx,')',Text)
            if Indx not in self.LneEdts.keys():
                self.LneEdts[Indx] = QLineEdit()
                self.EditBox.addWidget(self.LneEdts[Indx])
    
            self.LneEdts[Indx].setText(Text)
            
    
    # So here is how to make that (or any) GUI using straight Python-Qt
    class LoadUI(QWidget):
        def __init__(self, parent):
            QWidget.__init__(self)
            self.Parent = parent
            self.FileNameLst = []
    
            self.btnOpnImg = QPushButton('Open Image')
            self.btnOpnImg.clicked.connect(self.LoadImage)
            
            HBox1 = QHBoxLayout()
            HBox1.addWidget(self.btnOpnImg)
            HBox1.addStretch(1)
            
            Font = QFont()
            Font.setPointSize(16)
            Font.setBold(True)
            
            lblSelectd = QLabel('Your Selected Images:')
            lblSelectd.setFont(Font)
            
            HBox2 = QHBoxLayout()
            HBox2.addWidget(lblSelectd)
            HBox2.addStretch(1)
    
    # Comment-Out what you do not want to use and Uncomment-Out what you do
    # they have been made to be interchangable so you can see what each one
    # would look like
    
          # Now displaying the list of selected images could actually be done
          # several ways
          # 1) Your method of using QLineEdits
            self.OutPut = OutWithLineEdits(self)
    
          # 2) Using QLabels instead
    #        self.OutPut = OutWithLabels(self)
          # 3) Using a QTextEdit instead
    #        self.OutPut = OutWithTextEdit(self)
          # 4) Using a QListView instead
    #        self.OutPut = OutWithListView(self)
    
            VBox = QVBoxLayout()
            VBox.addLayout(HBox1)
            VBox.addLayout(HBox2)
            VBox.addWidget(self.OutPut)
            VBox.addStretch(1)
    
            self.setLayout(VBox)
    
      # This is just a simple redirect or pass through method used for
      # ease of reading as well as implementation as modifying is very
      # easy later on
        @pyqtSlot()
        def LoadImage(self):
            FileData, NotUsed = QFileDialog.getOpenFileNames(self, 'Select Multi File', 'default', 'All Files (*)')
    
            if len(FileData) > 0:
              # Enumeration was unnecessary, especially since it was not used
                for FileItem in FileData:
                  # Extract the File Name
                    BaseName = osPath.basename(FileItem)
                    self.FileNameLst.append(BaseName)
    
                    if not self.OutPut.PrimrSet:
                        self.OutPut.PrimrSet = True
                        Indx = 0
                    else:
                        self.OutPut.LstIndx += 1
                        Indx = self.OutPut.LstIndx
    
                    self.OutPut.SetImage(BaseName, Indx)
    
    
    # Keep your Main Window simple let it handle those things that are outside your
    # actual Graphic User Interface such MenuToolBar, StatusBar, and Dockable Windows
    # if you use any of these
    class Fenster(QMainWindow):
        def __init__(self):
          # One should not use super( ) in Python as it introduces 4 known issues that 
          # then must be handled properly. Further there were still actual bugs within
          # the usage of super( ) when used in Python as of early this year. So yes 
          # while super( ) works fine within C++ it does not work as seamlessly within
          # Python due to the major  differences between these two languages. Next the
          # reason it was created was  to handle a rather rare issue and unless you are
          # doing some complicated inheritance you will most likely never run into this
          # extremely rare issue. However the 4 major issues that get included by using
          # super( ) are much more likely to occur than that rare issue its meant for to
          # solve. Of course using the basic explicit method, as follows, does not cause
          # these issues and is as just as simple as using `super( )` further you do not
          # actually gain anything useful by using `super( )` in Python that could not be
          # done in a much safer manner.
            QMainWindow.__init__(self)
            self.setWindowTitle('Image Selector')
            WinLeft = 550; WinTop = 150; WinWidth = 350; WinHigh = 300
            self.setGeometry(WinLeft, WinTop, WinWidth, WinHigh)
    
          # The Center Pane is your actual Main Graphic User Interface
            self.CenterPane = LoadUI(self)
            self.setCentralWidget(self.CenterPane)
    
            self.StatBar = self.statusBar()
            
        def DsplyStatMsg(self, MsgTxt):
            self.StatBar.showMessage(MsgTxt)
    
    
    ######MAIN####################
    if __name__ == "__main__":
      # Unless you plan to do something with Command Line arguments no need
      # to include this and if you are you are better off using argparse 
      # library as it handles Command Line arguments much more cleanly
      # app = QApplication(sys.argv)
        MainEventThred = QApplication([])
    
        MainApplication = Fenster()
        MainApplication.show()
    
      # This is the Qt4 method of doing this    
      # sys.exit(app.exec_())
      # While this is the Qt5 method for doing this
        MainEventThred.exec()
    


  • @Denni-0 Hey, I thank you for the very detailed and helpful MUC. My later GUI looks much more complex than in the photo, which I showed as an example. Therefore I create the GUI with QT Designer (static). Is there still the possibility to change the GUI I created with QT Designer dynamically and add some textboxes to my main program ?

    With your MUC it is possible, but is it also possible to do this with a GUI that was created with Designer ?



  • @jsulm thank u very much. Would this also work if my GUI was created with QT Designer ? So I can overwrite the GUI with self.lineEdits += QLineEdit(self) afterwards with several textbox's ?


  • Lifetime Qt Champion

    @elias_hh I think if you want to modify your UI at runtime you will need to change your approach: do not use loadUi but instead use pyuic to generate Python code from ui file (which is XML).
    Take a look at https://doc.qt.io/qtforpython/tutorials/basictutorial/uifiles.html



  • @elias_hh
    I would recommend doing as @jsulm says. It is then easy to mix design-time & run-time widgets as desired, if that's what you want to do.



  • Hey, guys,

    Thanks for your help. I have decided to completely rethink my approach and work without Qt Designer. I will use the code from @Denni-0 as a base because it is almost exactly the way I want it to be. My gui should look like the following picture (designed with qt designer): Unbenannt.PNG

    My idea is that the program filters the required parameters from the names of the images. I know how to do that and it works. But as you can see in the picture I need two more QLineEdits(). One for Heights and one for Width. These act as security to see if the images are named correctly. If the user has named the images according to the correct nomenclature, the parameters appear there. If the image names were named incorrectly, a question mark appears there.

    But my main problem now is, I don't know how to position these textboxes next to the image name. I can only do it below. I tried with xyz.move(x,y) but the textboxes are still below. How can i solve this ?


  • Lifetime Qt Champion

    @elias_hh said in Dynamic adding of textbox in pyqt5:

    how to position these textboxes next to the image name

    You need to put them into the correct layout, don't use move().
    Please show your code and where you want to add text boxes.



  • @jsulm i threw out my code and want to use @Denni-0 . The only difference is that I want to have two more textboxes next to the textbox with the name (see figure).


  • Banned

    @elias_hh okay so how to render that Designer View in Python-Qt well here is a version of the above in a Layout form only hope this helps you moving forward

    from PyQt5.QtCore    import Qt, pyqtSlot
    from PyQt5.QtGui     import QFont
    from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget
    from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QStyleFactory
    from PyQt5.QtWidgets import QGridLayout, QLabel
    from PyQt5.QtWidgets import QPushButton, QLineEdit, QTextEdit
    
    
    class BigButton(QPushButton):
        def __init__(self, text):
            QPushButton.__init__(self)
    
            Font = QFont()
            Font.setPointSize(12)
            Font.setBold(True)
            
            Hight=50; Width=150
            self.setFixedSize(Width, Hight)
            self.setFont(Font)
            
            self.setText(text)
    
    # Note considering what you are doing with this I would
    # strongly suggest you replace the following with a QTreeView
    # with multiple columns as that would handle it much cleaner
    # and efficiently but here is literally what you presented
    class SelectdImgs(QWidget):
        def __init__(self, parent):
            QWidget.__init__(self)
    
            Font = QFont()
            Font.setBold(True)
    
            lblSelectd = QLabel('Selected Images')
            lblSelectd.setFont(Font)
    
            lblHight = QLabel('Height')
            lblHight.setFont(Font)
    
            lblWidth = QLabel('Width')
            lblWidth.setFont(Font)
            
            HiWi = 40
    
          # These could have been sub-classed like the buttons but
          # thought I would show both methods and why sub-classing
          # style elements can be very useful when they are repeated
            lneImgDesc1 = QLineEdit('Rectangle_abc_Height:5m_Width:8m')
          # Due to how the layout works, setting this minimum value 
          # for this one sets it for all of them
            lneImgDesc1.setMinimumWidth(200)
            lneImgHiht1 = QLineEdit('5')
            lneImgHiht1.setMaximumWidth(HiWi)
            lneImgHiht1.setAlignment(Qt.AlignHCenter)
            lneImgWdth1 = QLineEdit('8')
            lneImgWdth1.setMaximumWidth(HiWi)
            lneImgWdth1.setAlignment(Qt.AlignHCenter)
    
            lneImgDesc2 = QLineEdit('Rectangle_def_Height:8m_Width:8m')
            lneImgHiht2 = QLineEdit('8')
            lneImgHiht2.setMaximumWidth(HiWi)
            lneImgHiht2.setAlignment(Qt.AlignHCenter)
            lneImgWdth2 = QLineEdit('8')
            lneImgWdth2.setMaximumWidth(HiWi)
            lneImgWdth2.setAlignment(Qt.AlignHCenter)
    
            lneImgDesc3 = QLineEdit('Rectangle_ghi_Height:2m_Width:2m')
            lneImgHiht3 = QLineEdit('2')
            lneImgHiht3.setMaximumWidth(HiWi)
            lneImgHiht3.setAlignment(Qt.AlignHCenter)
            lneImgWdth3 = QLineEdit('2')
            lneImgWdth3.setMaximumWidth(HiWi)
            lneImgWdth3.setAlignment(Qt.AlignHCenter)
    
            lneImgDesc4 = QLineEdit('Rectangle_jkl_Height:5m_Width:4m')
            lneImgHiht4 = QLineEdit('5')
            lneImgHiht4.setMaximumWidth(HiWi)
            lneImgHiht4.setAlignment(Qt.AlignHCenter)
            lneImgWdth4 = QLineEdit('4')
            lneImgWdth4.setMaximumWidth(HiWi)
            lneImgWdth4.setAlignment(Qt.AlignHCenter)
    
            grdView = QGridLayout()
            grdView.addWidget(lblSelectd,0,0)
            grdView.addWidget(lblHight,0,1)
            grdView.addWidget(lblWidth,0,2)
    
            grdView.addWidget(lneImgDesc1,1,0)
            grdView.addWidget(lneImgHiht1,1,1)
            grdView.addWidget(lneImgWdth1,1,2)
    
            grdView.addWidget(lneImgDesc2,2,0)
            grdView.addWidget(lneImgHiht2,2,1)
            grdView.addWidget(lneImgWdth2,2,2)
    
            grdView.addWidget(lneImgDesc3,3,0)
            grdView.addWidget(lneImgHiht3,3,1)
            grdView.addWidget(lneImgWdth3,3,2)
    
            grdView.addWidget(lneImgDesc4,4,0)
            grdView.addWidget(lneImgHiht4,4,1)
            grdView.addWidget(lneImgWdth4,4,2)
    
            grdView.setColumnStretch(0, 1)
    
            self.setLayout(grdView)
    
    class CenterPanel(QWidget):
        def __init__(self, parent):
            QWidget.__init__(self)
            self.Parent = parent
    
            self.btnSelect = BigButton('Select')
            self.btnSelect.clicked.connect(self.Selectd)
            
            self.ImgView = SelectdImgs(self)
    
            self.btnCalc = BigButton('Start Calc')
            self.btnCalc.clicked.connect(self.CalcIt)
            
            self.txeOutput = QTextEdit()
            self.txeOutput.append('Rectangle_abc = 40 m²')
            self.txeOutput.append('Rectangle_def = 64 m²')
            self.txeOutput.append('Rectangle_ghi =  4 m²')
            self.txeOutput.append('Rectangle_jkl = 20 m²')
    
            VBox = QVBoxLayout()
            VBox.addWidget(self.btnSelect)
            VBox.addWidget(self.ImgView)
            VBox.addWidget(self.btnCalc)
            VBox.addWidget(self.txeOutput)
    
            self.setLayout(VBox)
    
        @pyqtSlot()
        def Selectd(self):
            self.Parent.SetStatusMsg('Item Selected')
        
        @pyqtSlot()
        def CalcIt(self):
            self.Parent.SetStatusMsg('Calculating Items')
    
    class MainWindow(QMainWindow):
        def __init__(self):
            QMainWindow.__init__(self)
    
            self.setWindowTitle('Main Window')
            WinLeft = 150; WinTop = 150; WinWidth = 500; WinHigh = 500
            self.setGeometry(WinLeft, WinTop, WinWidth, WinHigh)
    
            self.CenterPane = CenterPanel(self)
            self.setCentralWidget(self.CenterPane)
    
            self.StatBar = self.statusBar()
    
            self.setStyle(QStyleFactory.create('Cleanlooks'))
    
        def SetStatusMsg(self, StatusMsg=''):
            if len(StatusMsg) < 1:
                StatusMsg = 'Ready'
    
            self.StatBar.showMessage(StatusMsg)
    
    if __name__ == "__main__":
        MainEventThred = QApplication([])
    
        MainApplication = MainWindow()
        MainApplication.show()
    
        MainEventThred.exec()
    

Log in to reply