Pavel.D
Pavel.D

Reputation: 581

PyQt5 Image and QGridlayout

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_())

Upvotes: 4

Views: 5801

Answers (2)

eyllanesc
eyllanesc

Reputation: 243897

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_())

Upvotes: 3

S. Nick
S. Nick

Reputation: 13651

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

Upvotes: 0

Related Questions