PyQt5 图像和 QGridLayout

4

我有一个小部件,它希望使用QLabelQCheckBox显示图像。创建了4个类,每个类包含一些要放在最终屏幕上的信息。Grid类将图像、文本和复选框对齐和排列。

运行脚本后获取当前屏幕。当前小部件中没有图像。

enter image description here

图像在哪里?

期望的屏幕

enter image description here

代码如下

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_())
2个回答

3
S.Nick的解决方案可以工作,但不是正确的方法,此外它没有解释问题,只指出了你应该提供的错误消息。
你的第一个错误是没有一致的设计。例如,即使小部件是一个视觉元素,但并不意味着它只是一个视觉元素,它还有相关的数据。因此,我问自己:为什么你创建了ImageWidget类?同样的问题也适用于EffLengthInfoCheckInfo? 看起来只是为了创建一个布局,是必须要创建一个小部件吗?不是,因为小部件是一个不必要的资源,那就是浪费内存。因此,我指出S.Nick的解决方案是不正确的,因为他正在邀请其他人重叠设计错误。
如果你的目标只是创建布局,那么最好使用一个函数:
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_())

为什么EffLengthInfo和CheckInfo的案例中会出现“works”?

我指出“works”加引号是因为它虽然能工作,但并不是解决方案。事实上,在布局中建立了一个布局,并将其作为小部件的一部分,之后就不能将其放置在另一个小部件中。如果我们检查ImageWidget,我们会发现您已经将布局两次添加到小部件中:

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. 在构建布局时设置小部件,它将被设置在该小部件中。

  2. 当小部件使用setLayout()方法设置布局时

因此,在行vlay.addLayout(self.imagewidget.hlay)中应抛出以下错误:

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

另一种解决方案是设置小部件而不是布局:

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

另一个你可能想到的视角(这取决于你希望给小部件使用的方式),是你想要一个信息单元包含一张图片、一段文本和一个复选框。如果这是正确的,那么就可以创建一个小部件:

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

非常感谢。你的类和函数处理方式很棒。我一直在想addLayout和addWidget之间的区别,但是你的解释让我更容易理解了。我还没有调试代码,离电脑也比较远。稍后再回来。 - Pavel.D
@ZarKha 布局不是可见元素,它是一个类,允许建立位置和大小,而小部件则是一个视觉元素。布局处理项目,这些项目可以是小部件或其他布局,也就是说,最终它们用于建立某种几何形状。 - eyllanesc
@ZarKha [cont.] 仅拥有一个小部件来创建在另一个小部件中已经建立的布局是没有意义的,因为该小部件永远不会被显示,因此它永远不会有用。在编程中,这意味着需要花费内存和时间。如果您拥有Qt Designer,则会看到这些元素用线条表示,但在执行时不会显示,这些线条只是为了使您的组织易于理解。 - eyllanesc
1
谢谢。现在我已经运行了代码,它可以没有任何注释工作。我对布局和小部件的理解正在扩展。非常棒的反馈。 - Pavel.D

0

代码行:vlay.addLayout(self.imagewidget.hlay)

错误信息:QLayout::addChildLayout: layout "" 已经有一个父对象

尝试解决:

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


感谢您的回复和帮助。我还没有测试过更正后的代码,当我离开电脑时,我会稍后回复,看起来您的代码使用类很有效。 - Pavel.D

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接