Creating dynamic TextBox's

Heads up! You've already completed this tutorial.

Hamid_Rezaie | 2020-06-17 13:47:19 UTC | #1

Hey guys, I have the following MUC, which produces the following GUI (see Fig: left GUI).

Depending on how many images are selected, TextBoxes appear dynamically. The name of the image appears in these text boxes (see Fig: middle GUI).

Now everything is finde but i want to extend the GUI so that it extracts important parameters from the imagename and prints them right next to the TextBox containing the name in two more TextBoxes (because there are two parameters (height and width)). 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. It should look like the next image. At first three TextBox's in horizontal position. And if i select for example 3 Images, then in the next two rows should appear the same three TextBox's with particular imagename and parameters (see Fig 3: right GUI).

3|690x175

Here you can see the MUC:

python
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() ```

Luca | 2020-06-17 15:36:16 UTC | #2

I wrote an example using a QGridLayout:

python
from PyQt5.QtWidgets import *
import sys
import os

class ImageInfo():

    def __init__(self, index, filename, width, height, parent):
        self.parent = parent

        self.lineEditName = self.addLineEdit(filename, index, 0)
        self.lineEditWidth = self.addLineEdit(0, index, 1)
        self.lineEditHeight = self.addLineEdit(0, index, 2)

    def addLineEdit(self, text, row, col):
        lineEdit = QLineEdit(parent=self.parent)
        lineEdit.setText(str(text))
        self.parent.layout().addWidget(lineEdit, row, col)
        return lineEdit

class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.widget = QWidget()
        self.setCentralWidget(self.widget)
        self.layout = QGridLayout()
        self.widget.setLayout(self.layout)

        self.imagesInfo = []

        paths, _ = QFileDialog.getOpenFileNames(self, 'Select Multi File', 'default', 'All Files (*)')

        for index, path in enumerate(paths):
            filename, width, height = self.getImageInfo(path)
            self.imagesInfo = ImageInfo(index, filename, width, height, parent=self.widget)

    def getImageInfo(self, path):
        filename = os.path.basename(path)
        width, height = self.getImageSize(path)
        return filename, width, height

    def getImageSize(self, path):
        width, height = (0, 0) # Add your function
        return width, height

if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

An alternative could be: Change the layout into a QVBoxLayout Set ImageInfo class as a subclass of QWidget * Add the QLineEdits to a QHBoxLayout inside the ImageInfo class

Or another one using a QViewTable where each row is an image.

There are multiple solutions, that could be better, this was just a simple example to show you the concept.


The complete guide to packaging Python GUI applications with PyInstaller.
[[ discount.discount_pc ]]% OFF for the next [[ discount.duration ]] [[discount.description ]] with the code [[ discount.coupon_code ]]

Purchasing Power Parity

Developers in [[ country ]] get [[ discount.discount_pc ]]% OFF on all books & courses with code [[ discount.coupon_code ]]
Well done, you've finished this tutorial! Mark As Complete
[[ user.completed.length ]] completed [[ user.streak+1 ]] day streak

Creating dynamic TextBox's was written by Martin Fitzpatrick .

Martin Fitzpatrick has been developing Python/Qt apps for 8 years. Building desktop applications to make data-analysis tools more user-friendly, Python was the obvious choice. Starting with Tk, later moving to wxWidgets and finally adopting PyQt.