4

I've a Widget, which wants to display Images with QLabel and QCheckBox. 4 classes are created each contains some information to be put on the final screen. Class Grid align and grid images, text and checkboxes.

After script running get current screen. No images appear in present widget.

enter image description here

Where are images?

Desired screen

enter image description here

The code

import sys, os
from PyQt5 import QtCore, QtGui, QtWidgets

iconroot = os.path.dirname(__file__)

class ImageWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(ImageWidget, self).__init__(parent)

        self.hlay = QtWidgets.QHBoxLayout(self)
        buckling_ilabel = QtWidgets.QLabel(self)
        pixmap = QtGui.QPixmap(os.path.join(iconroot, "images/fixed-fixed.png"))
        buckling_ilabel.resize(150, 150)
        buckling_ilabel.setPixmap(pixmap.scaled(buckling_ilabel.size(), QtCore.Qt.KeepAspectRatio))
        self.hlay.addWidget(buckling_ilabel)

        buckling_blabel = QtWidgets.QLabel(self)
        pixmap = QtGui.QPixmap(os.path.join(iconroot, "images/pinned-pinned.png"))
        buckling_blabel.resize(150, 150)
        buckling_blabel.setPixmap(pixmap.scaled(buckling_blabel.size(), QtCore.Qt.KeepAspectRatio))
        self.hlay.addWidget(buckling_blabel)

        buckling_clabel = QtWidgets.QLabel(self)
        pixmap = QtGui.QPixmap(os.path.join(iconroot, "images/fixed-free.png"))
        buckling_clabel.resize(150, 150)
        buckling_clabel.setPixmap(pixmap.scaled(buckling_clabel.size(), QtCore.Qt.KeepAspectRatio))
        self.hlay.addWidget(buckling_clabel)

        buckling_dlabel = QtWidgets.QLabel(self)
        pixmap = QtGui.QPixmap(os.path.join(iconroot, "images/fixed-pinned.png"))
        buckling_dlabel.resize(150, 150)
        buckling_dlabel.setPixmap(pixmap.scaled(buckling_dlabel.size(), QtCore.Qt.KeepAspectRatio))
        self.hlay.addWidget(buckling_dlabel)
        self.setLayout(self.hlay)



class EffLengthInfo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(EffLengthInfo, self).__init__(parent)

        ilabel = QtWidgets.QLabel('Ley = 1.0 L\nLec = 1.0 L')
        blabel = QtWidgets.QLabel('Ley = 0.699 L\nLec = 0.699 L')
        clabel = QtWidgets.QLabel('Ley = 2.0 L\nLec = 2.0 L')   
        dlabel = QtWidgets.QLabel('Ley = 0.5 L\nLec = 0.5 L')

        self.ilay = QtWidgets.QHBoxLayout()
        self.ilay.addWidget(ilabel)
        self.ilay.addWidget(blabel)
        self.ilay.addWidget(clabel)
        self.ilay.addWidget(dlabel)


class CheckInfo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(CheckInfo, self).__init__(parent)

        icheck = QtWidgets.QCheckBox()
        bcheck = QtWidgets.QCheckBox()
        ccheck = QtWidgets.QCheckBox()
        dcheck = QtWidgets.QCheckBox()

        self.checklay = QtWidgets.QHBoxLayout()
        self.checklay.addWidget(icheck, alignment=QtCore.Qt.AlignCenter)
        self.checklay.addWidget(bcheck, alignment=QtCore.Qt.AlignCenter)
        self.checklay.addWidget(ccheck, alignment=QtCore.Qt.AlignCenter)
        self.checklay.addWidget(dcheck, alignment=QtCore.Qt.AlignCenter)



class Grid(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Grid, self).__init__(parent)

        self.imagewidget = ImageWidget()
        self.efflengthinfo = EffLengthInfo()
        self.checkinfo = CheckInfo()

        vlay = QtWidgets.QVBoxLayout(self)
        vlay.addLayout(self.imagewidget.hlay)
        vlay.addLayout(self.efflengthinfo.ilay)
        vlay.addLayout(self.checkinfo.checklay)
        self.setLayout(vlay)



if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    im = Grid()
    im.show()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Pavel.D
  • 561
  • 1
  • 15
  • 41

2 Answers2

3

The @S.Nick's solution works but it is not the correct one, besides that it does not explain the problem and it only indicates the error message that you should have provided.

The first mistake you have is that you do not have a consistent design, for example, even though a widget is a visual element, it does not only mean that it is only, it also has associated data, so I ask myself: Why did you create the ImageWidget class, the same thing? for EffLengthInfo and CheckInfo? Well it seems that just to create a layout, is it necessary to create a widget to just create a layout? No, because the widget is an unnecessary resource, that is, wasted memory. Therefore, I point out that S.Nick's solution is incorrect since he is inviting others to overlap a design error.

If your goal is just to create layouts then better use a function:

import sys, os
from PyQt5 import QtCore, QtGui, QtWidgets

iconroot = os.path.dirname(__file__)

def create_image_layout():
        hlay = QtWidgets.QHBoxLayout()
        for icon_name in ("images/fixed-fixed.png", 
                          "images/pinned-pinned.png",
                          "images/fixed-free.png",
                          "images/fixed-pinned.png"):

            label = QtWidgets.QLabel()
            pixmap = QtGui.QPixmap(os.path.join(iconroot, icon_name))
            label.resize(150, 150)
            label.setPixmap(pixmap.scaled(label.size(), QtCore.Qt.KeepAspectRatio))
            hlay.addWidget(label)
        return hlay

def create_EffLengthInfo_layout():
    hlay = QtWidgets.QHBoxLayout()
    for text in ('Ley = 1.0 L\nLec = 1.0 L',
                 'Ley = 0.699 L\nLec = 0.699 L',
                 'Ley = 2.0 L\nLec = 2.0 L',
                 'Ley = 0.5 L\nLec = 0.5 L'):
        label = QtWidgets.QLabel(text)
        hlay.addWidget(label)
    return hlay

def create_checkInfo_layout():
    hlay = QtWidgets.QHBoxLayout()
    for _ in range(3):
        checkbox = QtWidgets.QCheckBox()
        hlay.addWidget(checkbox, alignment=QtCore.Qt.AlignCenter)
    return hlay

class Grid(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Grid, self).__init__(parent)

        image_lay = create_image_layout()
        efflengthinfo_lay = create_EffLengthInfo_layout()
        checkinfo_lay = create_checkInfo_layout()

        vlay = QtWidgets.QVBoxLayout(self)
        vlay.addLayout(image_lay)
        vlay.addLayout(efflengthinfo_lay)
        vlay.addLayout(checkinfo_lay)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    im = Grid()
    im.show()
    sys.exit(app.exec_())

Why was it shown in the case of EffLengthInfo and CheckInfo "works"?

I point out that it works in quotes because it works but it is not the solution, going to the point, a layout is established in a layout and it is part of the widget and after that it can not be placed in another widget, and if we check ImageWidget we see that you have established two times the layout to the widget:

class ImageWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(ImageWidget, self).__init__(parent)

        self.hlay = QtWidgets.QHBoxLayout(self) # 1.
        ...
        self.setLayout(self.hlay) # 2.
  1. When you set a widget when building the layout, it is set in that widget.

  2. When the widget sets the layout using the setLayout() method

Therefore you should throw the following error in the line vlay.addLayout(self.imagewidget.hlay):

QLayout::addChildLayout: layout "" already has a parent

Another solution is to set the widgets instead of the layouts:

class ImageWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(ImageWidget, self).__init__(parent)
        self.hlay = QtWidgets.QHBoxLayout(self)
        ...
        # self.setLayout(self.hlay) # comment this line

class EffLengthInfo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(EffLengthInfo, self).__init__(parent)
        ...
        self.ilay = QtWidgets.QHBoxLayout(self) # <-- add self

class CheckInfo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(CheckInfo, self).__init__(parent)
        ...
        self.checklay = QtWidgets.QHBoxLayout(self)  # <-- add self

class Grid(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Grid, self).__init__(parent)
        self.imagewidget = ImageWidget()
        self.efflengthinfo = EffLengthInfo()
        self.checkinfo = CheckInfo()

        vlay = QtWidgets.QVBoxLayout(self)
        vlay.addWidget(self.imagewidget)  # <-- change addLayout() to addWidget()
        vlay.addWidget(self.efflengthinfo) # <-- change addLayout() to addWidget()
        vlay.addWidget(self.checkinfo) # <-- change addLayout() to addWidget()
        ...

Another perspective that you might think, and this depends on the use you want to give the widget, is that you want an information unit to be an image, a text and a checkbox, so if it is correct to create a widget:

import sys, os
from PyQt5 import QtCore, QtGui, QtWidgets

iconroot = os.path.dirname(__file__)


class FooWidget(QtWidgets.QWidget):
    def __init__(self, path_icon, text, checked=False, parent=None):
        super(FooWidget, self).__init__(parent)
        lay = QtWidgets.QVBoxLayout(self)

        pixmap = QtGui.QPixmap(os.path.join(iconroot, path_icon))
        pixmap_label = QtWidgets.QLabel()
        pixmap_label.resize(150, 150)
        pixmap_label.setPixmap(pixmap.scaled(pixmap_label.size(), QtCore.Qt.KeepAspectRatio))

        text_label = QtWidgets.QLabel(text)
        checkbox = QtWidgets.QCheckBox(checked=checked)

        lay.addWidget(pixmap_label)
        lay.addWidget(text_label)
        lay.addWidget(checkbox)


class Grid(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Grid, self).__init__(parent)

        lay = QtWidgets.QHBoxLayout(self)

        icons = ["images/fixed-fixed.png", 
                 "images/pinned-pinned.png",
                 "images/fixed-free.png",
                 "images/fixed-pinned.png"]

        texts = ["Ley = 1.0 L\nLec = 1.0 L",
                 "Ley = 0.699 L\nLec = 0.699 L",
                 "Ley = 2.0 L\nLec = 2.0 L",
                 "Ley = 0.5 L\nLec = 0.5 L"]

        for path_icon, text in zip(icons, texts):
            w = FooWidget(os.path.join(iconroot, path_icon), text)
            lay.addWidget(w)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    im = Grid()
    im.show()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thanks a lot. Your way come around classes and functions is great. I have in mind, what is the difference between addLayout and addWidget but your explaining eases understanding. I have not yet debugged the code, I am not near computer. I would be back later. – Pavel.D Oct 03 '18 at 05:45
  • @ZarKha A layout is not a visible element, it is a class that allows to establish positions and sizes, a widget instead is a visual element. the layouts handle items, and these items can be widgets or other layouts, that is, at the end they serve to establish a certain geometry. – eyllanesc Oct 03 '18 at 05:55
  • @ZarKha [cont.] having a widget only to create a layout that is established in another widget does not make sense, since the widget never gets it will show and therefore it will never be useful that in programming it implies spending of memory and time. If you have Qt Designer you will have seen that these elements are represented with lines but when they are executed they are not shown, the lines are a representation so that your organization is easily understood – eyllanesc Oct 03 '18 at 05:55
  • 1
    Thanks. Now I have run the code, it works with no notation. My understanding layout and widget is expanding. Great and fantastic feedback. – Pavel.D Oct 04 '18 at 09:24
0

Line: vlay.addLayout (self.imagewidget.hlay)

error is: QLayout :: addChildLayout: layout "" already has a parent

Try it:

import sys, os
from PyQt5 import QtCore, QtGui, QtWidgets

iconroot = os.path.dirname(__file__)

class ImageWidget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(ImageWidget, self).__init__(parent)

#        self.hlay = QtWidgets.QHBoxLayout(self)
        self.hlay = QtWidgets.QHBoxLayout()                       # < --- -self
        buckling_ilabel = QtWidgets.QLabel(self)
        pixmap = QtGui.QPixmap(os.path.join(iconroot, "images/logo.png"))
        buckling_ilabel.resize(150, 150)
        buckling_ilabel.setPixmap(pixmap.scaled(buckling_ilabel.size(), QtCore.Qt.KeepAspectRatio))
        self.hlay.addWidget(buckling_ilabel)

        buckling_blabel = QtWidgets.QLabel(self)
        pixmap = QtGui.QPixmap(os.path.join(iconroot, "images/logo.png"))
        buckling_blabel.resize(150, 150)
        buckling_blabel.setPixmap(pixmap.scaled(buckling_blabel.size(), QtCore.Qt.KeepAspectRatio))
        self.hlay.addWidget(buckling_blabel)

        buckling_clabel = QtWidgets.QLabel(self)
        pixmap = QtGui.QPixmap(os.path.join(iconroot, "images/logo.png"))
        buckling_clabel.resize(150, 150)
        buckling_clabel.setPixmap(pixmap.scaled(buckling_clabel.size(), QtCore.Qt.KeepAspectRatio))
        self.hlay.addWidget(buckling_clabel)

        buckling_dlabel = QtWidgets.QLabel(self)
        pixmap = QtGui.QPixmap(os.path.join(iconroot, "images/logo.png"))
        buckling_dlabel.resize(150, 150)
        buckling_dlabel.setPixmap(pixmap.scaled(buckling_dlabel.size(), QtCore.Qt.KeepAspectRatio))
        self.hlay.addWidget(buckling_dlabel)

#        self.setLayout(self.hlay)                                  # < ---




class EffLengthInfo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(EffLengthInfo, self).__init__(parent)

        ilabel = QtWidgets.QLabel('Ley = 1.0 L\nLec = 1.0 L')
        blabel = QtWidgets.QLabel('Ley = 0.699 L\nLec = 0.699 L')
        clabel = QtWidgets.QLabel('Ley = 2.0 L\nLec = 2.0 L')   
        dlabel = QtWidgets.QLabel('Ley = 0.5 L\nLec = 0.5 L')

        self.ilay = QtWidgets.QHBoxLayout()
        self.ilay.addWidget(ilabel)
        self.ilay.addWidget(blabel)
        self.ilay.addWidget(clabel)
        self.ilay.addWidget(dlabel)


class CheckInfo(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(CheckInfo, self).__init__(parent)

        icheck = QtWidgets.QCheckBox()
        bcheck = QtWidgets.QCheckBox()
        ccheck = QtWidgets.QCheckBox()
        dcheck = QtWidgets.QCheckBox()

        self.checklay = QtWidgets.QHBoxLayout()
        self.checklay.addWidget(icheck, alignment=QtCore.Qt.AlignCenter)
        self.checklay.addWidget(bcheck, alignment=QtCore.Qt.AlignCenter)
        self.checklay.addWidget(ccheck, alignment=QtCore.Qt.AlignCenter)
        self.checklay.addWidget(dcheck, alignment=QtCore.Qt.AlignCenter)



class Grid(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Grid, self).__init__(parent)

        self.imagewidget   = ImageWidget()
        self.efflengthinfo = EffLengthInfo()
        self.checkinfo     = CheckInfo()

        vlay = QtWidgets.QVBoxLayout(self) 

        vlay.addLayout(self.imagewidget.hlay)                            # < ---
        # Error: QLayout::addChildLayout: layout "" already has a parent # < ---

        vlay.addLayout(self.efflengthinfo.ilay)
        vlay.addLayout(self.checkinfo.checklay)
        self.setLayout(vlay)



if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    im = Grid()
    im.show()
    sys.exit(app.exec_())

enter image description here

S. Nick
  • 12,879
  • 8
  • 25
  • 33
  • Thanks for reply and help. I do not have tested the corrected code yet, when I am not near computer, I would reply later, and it seems your code works with classes. – Pavel.D Oct 03 '18 at 05:38