I want display my clickable image in vertical layout.i got similar example from google but i don't know how to get the image from clickable label. So can anyone please help me to add the image in vbox if i selected another items then the first item will remove in the vertical box.i want to display the last clickable item only. Here i tried so many ways but i am getting the object.Thank you in advance.
Given below is the example:
from PyQt4 import QtCore, QtGui
import functools
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
highlight_dir = './floder1'
scrollArea = QtGui.QScrollArea(widgetResizable=True)
self.setCentralWidget(scrollArea)
content_widget = QtGui.QWidget()
scrollArea.setWidget(content_widget)
self._layout = QtGui.QGridLayout(content_widget)
self._it = QtCore.QDirIterator(highlight_dir)
self.vbox = QtGui.QVBoxLayout()
self.mainLayout = QtGui.QGridLayout()
self.mainLayout.addLayout(self.vbox,0,1)
self.mainLayout.addWidget(scrollArea,0,0)
self.setCentralWidget(QtGui.QWidget(self))
self.centralWidget().setLayout(self.mainLayout)
self._row, self._col = 0, 0
QtCore.QTimer(self, interval=1, timeout=self.load_image).start()
def load_image(self):
if self._it.hasNext():
pixmap = QtGui.QPixmap(self._it.next())
if not pixmap.isNull():
vlay = QtGui.QVBoxLayout()
self.label_pixmap = QtGui.QLabel(alignment=QtCore.Qt.AlignCenter, pixmap=pixmap)
self.label_pixmap.mousePressEvent= functools.partial(self.item_discription,source_object=self.label_pixmap)
self.label_text = QtGui.QLabel(alignment=QtCore.Qt.AlignCenter, text=self._it.fileName())
print self.label_text.text()
vlay.addWidget(self.label_pixmap)
vlay.addWidget(self.label_text)
vlay.addStretch()
self._layout.addLayout(vlay, self._row, self._col)
self._col += 1
if self._col == 3:
self._row += 1
self._col = 0
def item_discription(self,event,source_object=None):
print self.label_text.text() #how to add clickable qlabel to vbox
self.vbox.addwidget(label_pixmap)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.setGeometry(500, 300, 900, 500)
w.show()
sys.exit(app.exec_())
If you want to make a QLabel notify you when there is a click, the simplest thing is to write it as indicated in this answer.
On the other hand it is better that you use a QScrollArea as the basis of the QVBoxLayout since having a lot will not be able to visualize them all.
Finally I prefer to use QSplitter since it allows to change the size of the widgets easily.
from PyQt4 import QtCore, QtGui
from functools import partial
class ClickableLabel(QtGui.QLabel):
clicked = QtCore.pyqtSignal()
def mousePressEvent(self, event):
self.clicked.emit()
super(ClickableLabel, self).mousePressEvent(event)
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scrollArea_left = QtGui.QScrollArea(widgetResizable=True)
content_widget_left = QtGui.QWidget()
scrollArea_left.setWidget(content_widget_left)
self._layout = QtGui.QGridLayout(content_widget_left)
scrollArea_right = QtGui.QScrollArea(widgetResizable=True)
content_widget_right = QtGui.QWidget()
scrollArea_right.setWidget(content_widget_right)
self._vbox = QtGui.QVBoxLayout(content_widget_right)
splitter = QtGui.QSplitter()
splitter.addWidget(scrollArea_left)
splitter.addWidget(scrollArea_right)
splitter.setStretchFactor(0, 3)
splitter.setStretchFactor(1, 1)
self.setCentralWidget(splitter)
highlight_dir = './floder1'
self._it = QtCore.QDirIterator(highlight_dir)
self._row, self._col = 0, 0
QtCore.QTimer(self, interval=1, timeout=self.load_image).start()
def load_image(self):
if self._it.hasNext():
pixmap = QtGui.QPixmap(self._it.next())
if pixmap.isNull():
return
vlay = QtGui.QVBoxLayout()
label_pixmap = ClickableLabel(alignment=QtCore.Qt.AlignCenter, pixmap=pixmap)
label_text = QtGui.QLabel(alignment=QtCore.Qt.AlignCenter, text=self._it.fileName())
label_pixmap.clicked.connect(partial(self.on_clicked, self._it.fileName(), pixmap))
vlay.addWidget(label_pixmap)
vlay.addWidget(label_text)
vlay.addStretch()
self._layout.addLayout(vlay, self._row, self._col)
self._col += 1
if self._col == 3:
self._row += 1
self._col = 0
def on_clicked(self, text, pixmap):
vlay = QtGui.QVBoxLayout()
vlay.addWidget(QtGui.QLabel(alignment=QtCore.Qt.AlignCenter, text=text))
vlay.addWidget(QtGui.QLabel(alignment=QtCore.Qt.AlignCenter, pixmap=pixmap))
self._vbox.addLayout(vlay)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = MainWindow()
w.showMaximized()
sys.exit(app.exec_())
Update:
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scrollArea_left = QtGui.QScrollArea(widgetResizable=True)
content_widget_left = QtGui.QWidget()
scrollArea_left.setWidget(content_widget_left)
self._layout = QtGui.QGridLayout(content_widget_left)
self._label_pixmap = QtGui.QLabel(alignment=QtCore.Qt.AlignCenter)
self._label_text = QtGui.QLabel(alignment=QtCore.Qt.AlignCenter)
widget = QtGui.QWidget()
vbox = QtGui.QVBoxLayout(widget)
vbox.addWidget(self._label_pixmap)
vbox.addWidget(self._label_text)
splitter = QtGui.QSplitter()
splitter.addWidget(scrollArea_left)
splitter.addWidget(widget)
splitter.setStretchFactor(0, 3)
splitter.setStretchFactor(1, 1)
self.setCentralWidget(splitter)
highlight_dir = './floder1'
self._it = QtCore.QDirIterator(highlight_dir)
self._row, self._col = 0, 0
QtCore.QTimer(self, interval=1, timeout=self.load_image).start()
def load_image(self):
if self._it.hasNext():
pixmap = QtGui.QPixmap(self._it.next())
if pixmap.isNull():
return
vlay = QtGui.QVBoxLayout()
label_pixmap = ClickableLabel(alignment=QtCore.Qt.AlignCenter, pixmap=pixmap)
label_text = QtGui.QLabel(alignment=QtCore.Qt.AlignCenter, text=self._it.fileName())
label_pixmap.clicked.connect(partial(self.on_clicked, self._it.fileName(), pixmap))
vlay.addWidget(label_pixmap)
vlay.addWidget(label_text)
vlay.addStretch()
self._layout.addLayout(vlay, self._row, self._col)
self._col += 1
if self._col == 3:
self._row += 1
self._col = 0
def on_clicked(self, text, pixmap):
self._label_pixmap.setPixmap(pixmap)
self._label_text.setText(text)
Related
This question already has an answer here:
QStackedWidget - Change page one by one
(1 answer)
Closed 1 year ago.
I'm making a multipage application in which I require to navigate between pages using QStackedLayout. I've made two buttons Previous and Next to navigate.
The problem is that the buttons stop functioning after 2-3 clicks. I guess it's got something to do with the way I'm trying to retrieve the value of CurrentIndex() in the if statement.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Window(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.frame = QtWidgets.QFrame()
self.stack_lay = QtWidgets.QStackedLayout()
cam_widget = Page1(self)
self.stack_lay.addWidget(cam_widget)
self.stack_lay.setCurrentWidget(cam_widget)
self.frame.setLayout(self.stack_lay)
self.previous = QtWidgets.QPushButton('Previous')
self.previous.clicked.connect(self.previous_page)
self.previous.setDisabled(True)
self.next = QtWidgets.QPushButton('Next')
self.next.clicked.connect(self.next_page)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.frame)
layout.addWidget(self.previous)
layout.addWidget(self.next)
self.setLayout(layout)
def previous_page(self):
if self.stack_lay.currentIndex() == 1:
page1_widget = Page1(self)
self.stack_lay.addWidget(page1_widget)
self.stack_lay.setCurrentWidget(page1_widget)
self.previous.setDisabled(True)
self.next.setDisabled(False)
elif self.stack_lay.currentIndex() == 2:
page2_widget = Page2(self)
self.stack_lay.addWidget(page2_widget)
self.stack_lay.setCurrentWidget(page2_widget)
self.previous.setDisabled(False)
self.next.setDisabled(False)
def next_page(self):
if self.stack_lay.currentIndex() == 0:
page2_widget = Page2(self)
self.stack_lay.addWidget(page2_widget)
self.stack_lay.setCurrentWidget(page2_widget)
self.previous.setDisabled(False)
self.next.setDisabled(False)
elif self.stack_lay.currentIndex() == 1:
page3_widget = Page3(self)
self.stack_lay.addWidget(page3_widget)
self.stack_lay.setCurrentWidget(page3_widget)
self.previous.setDisabled(False)
self.next.setDisabled(True)
class Page1(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Page1, self).__init__(parent)
label = QtWidgets.QLabel('Page 1')
lay = QtWidgets.QVBoxLayout()
lay.addWidget(label)
self.setLayout(lay)
class Page2(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Page2, self).__init__(parent)
label = QtWidgets.QLabel('Page 2')
lay = QtWidgets.QVBoxLayout()
lay.addWidget(label)
self.setLayout(lay)
class Page3(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Page3, self).__init__(parent)
label = QtWidgets.QLabel('Page 3')
lay = QtWidgets.QVBoxLayout()
lay.addWidget(label)
self.setLayout(lay)
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = Window()
window.show()
sys.exit(app.exec_())
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Window(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
self.frame = QtWidgets.QFrame()
self.stack_lay = QtWidgets.QStackedLayout()
self.page1_widget = Page1(self)
self.page2_widget = Page2(self)
self.page3_widget = Page3(self)
self.stack_lay.addWidget(self.page1_widget)
self.stack_lay.addWidget(self.page2_widget)
self.stack_lay.addWidget(self.page3_widget)
self.stack_lay.setCurrentWidget(self.page1_widget)
self.frame.setLayout(self.stack_lay)
self.previous = QtWidgets.QPushButton('Previous')
self.previous.clicked.connect(self.previous_page)
self.previous.setDisabled(True)
self.next = QtWidgets.QPushButton('Next')
self.next.clicked.connect(self.next_page)
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.frame)
layout.addWidget(self.previous)
layout.addWidget(self.next)
self.setLayout(layout)
def previous_page(self):
if self.stack_lay.currentIndex() == 1:
self.stack_lay.setCurrentWidget(self.page1_widget)
self.previous.setDisabled(True)
self.next.setDisabled(False)
elif self.stack_lay.currentIndex() == 2:
self.stack_lay.setCurrentWidget(self.page2_widget)
self.previous.setDisabled(False)
self.next.setDisabled(False)
def next_page(self):
if self.stack_lay.currentIndex() == 0:
self.stack_lay.setCurrentWidget(self.page2_widget)
self.previous.setDisabled(False)
self.next.setDisabled(False)
elif self.stack_lay.currentIndex() == 1:
self.stack_lay.setCurrentWidget(self.page3_widget)
self.previous.setDisabled(False)
self.next.setDisabled(True)
class Page1(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Page1, self).__init__(parent)
label = QtWidgets.QLabel('Page 1')
lay = QtWidgets.QVBoxLayout()
lay.addWidget(label)
self.setLayout(lay)
class Page2(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Page2, self).__init__(parent)
label = QtWidgets.QLabel('Page 2')
lay = QtWidgets.QVBoxLayout()
lay.addWidget(label)
self.setLayout(lay)
class Page3(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Page3, self).__init__(parent)
label = QtWidgets.QLabel('Page 3')
lay = QtWidgets.QVBoxLayout()
lay.addWidget(label)
self.setLayout(lay)
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = Window()
window.show()
sys.exit(app.exec_())
I have the following toy interface:
from PyQt5 import QtWidgets, QtGui, QtCore
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
w = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout()
w.setLayout(layout)
self.setCentralWidget(w)
my_tree = QtWidgets.QTreeWidget()
layout.addWidget(my_tree)
alpha = QtWidgets.QTreeWidgetItem(my_tree, ['Alpha'])
beta = QtWidgets.QTreeWidgetItem(my_tree, ['Beta'])
alpha.addChild(QtWidgets.QTreeWidgetItem(['one']))
alpha.addChild(QtWidgets.QTreeWidgetItem(['two']))
beta.addChild(QtWidgets.QTreeWidgetItem(['first']))
beta.addChild(QtWidgets.QTreeWidgetItem(['second']))
my_tree.expandAll()
alpha.child(0).setSelected(True)
scroll = QtWidgets.QScrollArea()
layout.addWidget(scroll)
scrollLayout = QtWidgets.QVBoxLayout()
scrollW = QtWidgets.QWidget()
scroll.setWidget(scrollW)
scrollW.setLayout(scrollLayout)
scrollLayout.setAlignment(QtCore.Qt.AlignTop)
scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
scroll.setWidgetResizable(True)
for _ in range(5):
fooGroup = QtWidgets.QGroupBox()
fooLayout = QtWidgets.QVBoxLayout()
fooGroup.setLayout(fooLayout)
fooItem1 = QtWidgets.QLabel("fooItem1")
fooItem2 = QtWidgets.QLabel("fooItem2")
fooItem3 = QtWidgets.QLabel("fooItem3")
fooLayout.addWidget(fooItem1)
fooLayout.addWidget(fooItem2)
fooLayout.addWidget(fooItem3)
scrollLayout.addWidget(fooGroup)
self.show()
app = QtWidgets.QApplication([])
window = MainWindow()
app.exec_()
How can I make each group in the scroll area selectable and clickable by the user?
I have so far tried to add the following code in the loop:
def onFooGroupClick():
print("Group")
fooGroup.clicked.connect(onFooGroupClick)
and (as per this post):
def onFooGroupClick():
print("Group")
def f():
return onFooGroupClick()
fooGroup.mousePressEvent = f()
However, all my efforts have been unsuccessful and I cannot seem to be able to make it work.
Create a class that inherits from QGroupBox.
Define the clicked signal in it and override the mousePressEvent method.
from PyQt5 import QtWidgets, QtGui, QtCore
class GroupBox(QtWidgets.QGroupBox): # +++ !!!
clicked = QtCore.pyqtSignal(str, object) # +++
def __init__(self, title):
super(GroupBox, self).__init__()
self.title = title
self.setTitle(self.title)
def mousePressEvent(self, event):
child = self.childAt(event.pos())
if not child:
child = self
self.clicked.emit(self.title, child) # +++
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
w = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout()
w.setLayout(layout)
self.setCentralWidget(w)
my_tree = QtWidgets.QTreeWidget()
layout.addWidget(my_tree)
alpha = QtWidgets.QTreeWidgetItem(my_tree, ['Alpha'])
beta = QtWidgets.QTreeWidgetItem(my_tree, ['Beta'])
alpha.addChild(QtWidgets.QTreeWidgetItem(['one']))
alpha.addChild(QtWidgets.QTreeWidgetItem(['two']))
beta.addChild(QtWidgets.QTreeWidgetItem(['first']))
beta.addChild(QtWidgets.QTreeWidgetItem(['second']))
my_tree.expandAll()
alpha.child(0).setSelected(True)
scroll = QtWidgets.QScrollArea()
layout.addWidget(scroll)
scrollLayout = QtWidgets.QVBoxLayout()
scrollW = QtWidgets.QWidget()
scroll.setWidget(scrollW)
scrollW.setLayout(scrollLayout)
scrollLayout.setAlignment(QtCore.Qt.AlignTop)
scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
scroll.setWidgetResizable(True)
for _ in range(5):
fooGroup = GroupBox(f'GroupBox_{_}') # - QtWidgets.QGroupBox()
fooGroup.setObjectName(f'fooGroup {_}')
fooGroup.clicked.connect(self.onFooGroupClick) # +++
fooLayout = QtWidgets.QVBoxLayout()
fooGroup.setLayout(fooLayout)
fooItem1 = QtWidgets.QLabel("fooItem1", objectName="fooItem1")
fooItem1.setStyleSheet('background: #44ffff')
fooItem2 = QtWidgets.QLabel("fooItem2", objectName="fooItem2")
fooItem2.setStyleSheet('background: #ffff56;')
fooItem3 = QtWidgets.QLabel("fooItem3", objectName="fooItem3")
fooItem3.setStyleSheet('background: #ff42ff;')
fooLayout.addWidget(fooItem1)
fooLayout.addWidget(fooItem2)
fooLayout.addWidget(fooItem3)
scrollLayout.addWidget(fooGroup)
def onFooGroupClick(self, title, obj): # +++
print(f"Group: {title}; objectName=`{obj.objectName()}`")
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = MainWindow()
window.show()
app.exec_()
Here is my sample code,i want to add a push button to each and every row of the list view.I am not found any method to set the widget to model.Can any one please help me how to add widget for each and every row of the list view.Thank you in advance.
Given below is my code:
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class MyCustomWidget(QWidget):
def __init__(self,parent=None):
super(MyCustomWidget, self).__init__(parent)
self.row = QHBoxLayout()
self.row.addWidget(QPushButton("add"))
self.setLayout(self.row)
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent=parent)
vLayout = QtGui.QVBoxLayout(self)
hLayout = QtGui.QHBoxLayout()
self.lineEdit = QtGui.QLineEdit()
hLayout.addWidget(self.lineEdit)
self.filter = QtGui.QPushButton("filter", self)
hLayout.addWidget(self.filter)
self.list = QtGui.QListView(self)
vLayout.addLayout(hLayout)
vLayout.addWidget(self.list)
self.model = QtGui.QStandardItemModel(self.list)
codes = [
'windows',
'windows xp',
'windows7',
'hai',
'habit',
'hack',
'good'
]
self.list.setModel(self.model)
for code in codes:
item = QtGui.QStandardItem(code)
self.model.appendRow(item)
self.list.setIndexWidget(item.index(), QtGui.QPushButton("button"))
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = Dialog()
w.show()
sys.exit(app.exec_())
You have to create a custom widget where you must set the button on the right side with a layout.
import sys
from PyQt4 import QtCore, QtGui
class CustomWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(CustomWidget, self).__init__(parent)
self.button = QtGui.QPushButton("button")
lay = QtGui.QHBoxLayout(self)
lay.addWidget(self.button, alignment=QtCore.Qt.AlignRight)
lay.setContentsMargins(0, 0, 0, 0)
class Dialog(QtGui.QDialog):
def __init__(self, parent=None):
super(Dialog, self).__init__(parent=parent)
vLayout = QtGui.QVBoxLayout(self)
hLayout = QtGui.QHBoxLayout()
self.lineEdit = QtGui.QLineEdit()
hLayout.addWidget(self.lineEdit)
self.filter = QtGui.QPushButton("filter", self)
hLayout.addWidget(self.filter)
self.list = QtGui.QListView(self)
vLayout.addLayout(hLayout)
vLayout.addWidget(self.list)
self.model = QtGui.QStandardItemModel(self.list)
codes = [
'windows',
'windows xp',
'windows7',
'hai',
'habit',
'hack',
'good'
]
self.list.setModel(self.model)
for code in codes:
item = QtGui.QStandardItem(code)
self.model.appendRow(item)
self.list.setIndexWidget(item.index(), CustomWidget())
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = Dialog()
w.show()
sys.exit(app.exec_())
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.
Where are images?
Desired screen
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_())
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.
When you set a widget when building the layout, it is set in that widget.
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_())
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_())
I tried to make some simple local chatting app for practice. I make a button to send a message every time i push the button the message displayed but the scrollarea become more narrow and not expanded the scrollarea. Am I missing something or added something i shouldn't in my code? how can i fix this?
Here is my code:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Bubble(QLabel):
def __init__(self,text):
super(Bubble,self).__init__(text)
self.setContentsMargins(5,5,5,5)
def paintEvent(self, e):
p = QPainter(self)
p.setRenderHint(QPainter.Antialiasing,True)
p.drawRoundedRect(0,0,self.width()-1,self.height()-1,5,5)
super(Bubble,self).paintEvent(e)
class MyWidget(QWidget):
def __init__(self,text,left=True):
super(MyWidget,self).__init__()
hbox = QHBoxLayout()
label = Bubble(text)
if not left:
hbox.addSpacerItem(QSpacerItem(1,1,QSizePolicy.Expanding,QSizePolicy.Preferred))
hbox.addWidget(label)
if left:
hbox.addSpacerItem(QSpacerItem(1,1,QSizePolicy.Expanding,QSizePolicy.Preferred))
hbox.setContentsMargins(0,0,0,0)
self.setLayout(hbox)
self.setContentsMargins(0,0,0,0)
class Chatting(QWidget):
def __init__(self, parent=None):
super(Chatting, self).__init__(parent)
self.vbox = QVBoxLayout()
for _ in range(20):
self.vbox.addWidget(MyWidget("Left side"))
widget = QWidget()
widget.setLayout(self.vbox)
scroll = QScrollArea()
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scroll.setWidgetResizable(False)
scroll.setWidget(widget)
#Scroll Area Layer add
vLayout = QVBoxLayout(self)
vLayout.addWidget(scroll)
send = QPushButton('')
send.setIcon(QIcon('images/send.png'))
send.setStyleSheet("background-color:#d00001; width: 44px")
send.setIconSize(QSize(84,20))
send.clicked.connect(self.send_messages)
vLayout.addWidget(send)
self.setLayout(vLayout)
def send_messages(self):
self.vbox.addWidget(MyWidget('testing'))
The problem is simple you have to enable the widgetResizable property to True since that property allows the QScrollArea to calculate the size of the content.
scroll.setWidgetResizable(True)
On the other hand I have taken the time to improve your code and I show it below:
from PyQt4 import QtGui, QtCore
class Bubble(QtGui.QLabel):
def __init__(self, text):
super(Bubble,self).__init__(text)
self.setContentsMargins(5, 5, 5, 5)
def paintEvent(self, e):
p = QtGui.QPainter(self)
p.setRenderHint(QtGui.QPainter.Antialiasing, True)
p.drawRoundedRect(self.rect().adjusted(0, 0, -1, -1), 5, 5)
super(Bubble, self).paintEvent(e)
class MyWidget(QtGui.QWidget):
def __init__(self, text, left=True):
super(MyWidget,self).__init__()
lay = QtGui.QVBoxLayout(self)
lay.setContentsMargins(0, 0, 0, 0)
self.setContentsMargins(0, 0, 0, 0)
label = Bubble(text)
lay.addWidget(label, alignment= QtCore.Qt.AlignLeft if left else QtCore.Qt.AlignRight)
class Chatting(QtGui.QWidget):
def __init__(self, parent=None):
super(Chatting, self).__init__(parent)
widget = QtGui.QWidget()
self.vbox = QtGui.QVBoxLayout(widget)
self.vbox.addStretch()
self.scroll = QtGui.QScrollArea(widgetResizable=True)
self.scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.scroll.setWidget(widget)
#Scroll Area Layer add
send = QtGui.QPushButton(icon= QtGui.QIcon('images/send.png'))
send.setStyleSheet("background-color:#d00001; width: 44px")
send.setIconSize(QtCore.QSize(84,20))
send.clicked.connect(self.on_clicked)
vLayout = QtGui.QVBoxLayout(self)
vLayout.addWidget(self.scroll)
vLayout.addWidget(send)
def send_message(self, text, direction=True):
widget = MyWidget(text, direction)
self.vbox.insertWidget(self.vbox.count()-1, widget)
scroll_bar = self.scroll.verticalScrollBar()
# move to end
QtCore.QTimer.singleShot(10, lambda: scroll_bar.setValue(scroll_bar.maximum()))
#QtCore.pyqtSlot()
def on_clicked(self):
self.send_message("testing")
if __name__ == '__main__':
import sys
import random
app = QtGui.QApplication(sys.argv)
w = Chatting()
def test():
for _ in range(8):
w.send_message("testing", random.choice((True, False)))
QtCore.QTimer.singleShot(1000, test)
w.resize(240, 480)
w.show()
sys.exit(app.exec_())