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

Creating dynamic TextBox's



  • Hey guys, I have the following MUC (tanks @Denni-0), which produces the following GUI (see picture).
    2.PNG
    Depending on how many images are selected, TextBoxes appear dynamically. The name of the image appears in these text boxes.

    1.PNG

    Now I want to extend the GUI so that it extracts important parameters from the name and prints them right next to the TextBox containing the name in two more TextBoxes (because there are two parameters). I know how to extract the parameters, it works. But I do not know how to dynamically add two more TextBoxes horizontally. Then i use the Parameter to calculate the area. But I can't change the MUC so that it dynamically adds the desired TextBox's (see Fig below).
    Unbenannt.PNG

    This is the MUC. You can copy it and test it:

    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 QGridLayout
    from PyQt5.QtWidgets import QListView
    
    from os import path as osPath
    
    # 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() ```


  • @elias_hh
    Please give your widgets (and any other variables) a meaningful name. self.LneEdits and self.LE don't help anyone trying to understand the code: something more like, say, leSelectedImages and leHeights, leWidths for the arrays is going to be a lot more helpful for other people, and hopefully you too, to understand what's going on! :)

    Now that I have got that: your

                self.LneEdts[Indx] = QLineEdit()
                self.LE[Indx] = QLineEdit()
                self.EditBox.addWidget(self.LneEdts[Indx])
    

    adds the LneEdts[Indx] onto the EditBox widget, but you do nothing at all with the QLineEdit put into self.LE[Indx]. Obviously you must realise that the extra widgets need adding too.

    Meanwhile, I see self.EditBox = QVBoxLayout(), so EditBox has a vertical layout, onto which self.LneEdts[Indx] is added. Hence these line edits will be added vertically, one under the other on a new row. If you just add the Height/Width line edits onto EditBox too they will come out under the Selected Images names line edits, which is not what you will want.

    Your EditBox will need its current QVBoxLayout to be vertical, but that will now need a QHBoxLayout added for each row, and it is that onto which you will want to add all your QLineEdits, so that they come horizontally on each row.

    Hence your adding code will want to look something like:

            if index not in self.leSelectedImages.keys():
                self.leSelectedImages[index] = QLineEdit()
                self.leHeights[index] = QLineEdit()
                self.leWidths[index] = QLineEdit()
                hBoxLayout = QHBoxLayout()
                hBoxLayout.addWidget(self.leSelectedImages[index])
                hBoxLayout.addWidget(self.leHeights[index])
                hBoxLayout.addWidget(self.leWidths[index])
                self.editBoxLayout.addLayout(hBoxLayout)
    

    See how we can use self.editBoxLayout.addLayout(hBoxLayout) to append a (horizontal) layout onto a (vertical) layout --- you can add other layouts, not just widgets, onto layouts.

    You might want to change over your vertical + horizontal layouts to use a QGridLayout instead, but I have left this using Q[HV]BoxLayout.

    P.S.
    BTW, in the code you posted you are missing the very first line, required for your @pyqtSlot decorator, which should read

    from PyQt5.QtCore import Qt, pyqtSlot
    


  • Hey @JonB, I must apologize for the confusion with self.LE[Indx] = QLineEdit(). Because I forgot to delete them from the code. This has no particular meaning.

    I looked at your suggested code and understood it. However, if I try to implement your code in mine, it unfortunately does not work. I must be doing something wrong. Maybe I'm putting it in the wrong place. Could you possibly tell me where you would insert your suggestion in my code ?



  • @elias_hh
    In your def SetImage(), exactly where you presently have if Indx not in self.LneEdts.keys():, since that's where you want it!

    Note that I (deliberately) did not use the spelling/capitalisation of your current code (because it's non-standard). You are expected to do whatever to change you code or my code to make them correspond as necessary.



  • @JonB Ok Thank you, i will try it ! :D


Log in to reply