pyqt5: some widgets are disappear in my label - python

I am implement my-self label, but some widget is disappear.
My code is:
from PyQt5.QtWidgets import *
import sys
class TypeManagerLabel(QLabel):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
self.setLayout(layout)
btnLayout = QHBoxLayout()
self.__nameLineEdit = QLineEdit()
self.__opBtn = QPushButton('Add/Remove')
self.__colorBtn = QPushButton('Color')
btnLayout.addWidget(self.__nameLineEdit)
btnLayout.addWidget(self.__opBtn)
layout.addLayout(btnLayout)
class MyWin(QLabel):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
self.setLayout(layout)
lab1 = TypeManagerLabel()
layout.addWidget(lab1)
# layout.addStretch()
lab2 = QPushButton('test')
layout.addWidget(lab2)
app = QApplication(sys.argv)
dialog = MyWin()
dialog.show()
app.exec_()
Currently, the label is OK, and the result should be:
Now, I want the QLineEdit should be located on the top of the label, thus I add a stretch. And the code is:
class MyWin(QLabel):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
self.setLayout(layout)
lab1 = TypeManagerLabel()
layout.addWidget(lab1)
layout.addStretch() ###################### add the stretch
lab2 = QPushButton('test')
layout.addWidget(lab2)
And the result is:
In the above figure, the QLineEdit is disappeared.
#
My environment is:
win 10
python 3.7.8
pyqt5 5.14.0
--------------------------------update ----------------------------------
Thank for the suggestion from musicamante and Heike, subclass QWidget instead of QLabel. But the new bug is reported after I add some new widget. The code is:
from PyQt5.QtWidgets import *
import sys
class TypeManagerLabel(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
self.setLayout(layout)
btnLayout = QHBoxLayout()
self.__nameLineEdit = QLineEdit()
self.__opBtn = QPushButton('Add')
btnLayout.addWidget(self.__nameLineEdit)
btnLayout.addWidget(self.__opBtn)
layout.addLayout(btnLayout)
infoLabel = QLabel()
layout.addWidget(infoLabel)
self.__infoLayout = QVBoxLayout()
infoLabel.setLayout(self.__infoLayout)
self.__opBtn.clicked.connect(self.addRemoveSlot)
def addRemoveSlot(self, checked=False):
name = self.__nameLineEdit.text()
layout = QHBoxLayout()
checkBox = QCheckBox()
lineEdit = QLineEdit(name)
layout.addWidget(checkBox)
layout.addWidget(lineEdit)
self.__infoLayout.addLayout(layout)
class MyWin(QLabel):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
self.setLayout(layout)
lab1 = TypeManagerLabel()
layout.addWidget(lab1)
# layout.addStretch()
lab2 = QPushButton('test')
layout.addWidget(lab2)
app = QApplication(sys.argv)
dialog = MyWin()
dialog.show()
app.exec_()
When I input a string in the QLineEdit, and click the "Add" button, the result is:
The above figure is what I expected.
But if I add stretch with the code:
class MyWin(QLabel):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
self.setLayout(layout)
lab1 = TypeManagerLabel()
layout.addWidget(lab1)
layout.addStretch() ###################### add the stretch
lab2 = QPushButton('test')
layout.addWidget(lab2)
Then, if I input a string in the QLineEdit and click the 'Add' button, the expected widget would not appear.

QLabel is a very special type of widget. While it seems very simple, it is not: it has its own behavior when dealing with sizes, and that's in order to accomodate all requirements a widget that is primarily based on (possibly) variable text size, not only horizontally, but vertically also.
That said, one should never try to add layouts and child widgets to classes that are not intended to be used as container, most importantly it should not be done on widgets with peculiar behavior like QLabel.
Using such a widget to contain other widgets is not only a very bad idea, but also completely useless, as you're not actually using the real features a QLabel provides (showing text or images).
To add children and layouts, just use a nested layout, a plain QWidget class, or any other container widgets like QGroupBox or QFrame.
Even after the comments, you're still trying to add widgets to a QLabel. Remove that label, and just add the layout to the main one.
class TypeManagerLabel(QWidget):
def __init__(self):
super().__init__()
# ...
layout.addLayout(btnLayout)
self.__infoLayout = QVBoxLayout()
layout.addLayout(self.__infoLayout)
self.__opBtn.clicked.connect(self.addRemoveSlot)
# ...

Related

PyQt5 Example of Using `QSizePolicy` to Shrink a Widget

I am trying to stop a widget from expanding by setting its size policy but things are not working.
The following code runs:
from PyQt5.QtWidgets import*
import pyqtgraph as pg
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.layout = QHBoxLayout()
self.left_widget = pg.GraphicsLayoutWidget()
self.layout.addWidget(self.left_widget)
self.right_widget = RightWidget()
#self.right_widget.groupbox.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
self.layout.addWidget(self.right_widget)
self.widget = QWidget()
self.widget.setLayout(self.layout)
self.setCentralWidget(self.widget)
class RightWidget(QWidget):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.groupbox = QGroupBox()
self.groupbox.setTitle("some title")
#self.groupbox.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
self.groupboxlayout = QVBoxLayout()
self.button1 = QPushButton ("1")
self.groupboxlayout.addWidget(self.button1)
self.button2 = QPushButton ("2")
self.groupboxlayout.addWidget(self.button2)
self.button3 = QPushButton ("3")
self.groupboxlayout.addWidget(self.button3)
self.groupbox.setLayout(self.groupboxlayout)
self.layout.addWidget(self.groupbox)
self.button4 = QPushButton ("4")
self.layout.addWidget(self.button4)
self.button5 = QPushButton ("5")
self.layout.addWidget(self.button5)
self.setLayout(self.layout)
if __name__ == '__main__':
import sys
system_app = QApplication(sys.argv)
main = MainWindow()
main.show()
system_app.exec()
It gives:
The specific choice of widgets should not matter. The point is that, there is a large widget on the left and there are smaller widgets on the right that is not large enough to fill the column. I want to modify the above code to shrink the widgets on the right hand side. That is:
In words, I want the widgets to shrink to top and bottom of the page in a way taking minimum space (and hence leaving the middle empty).
I attempted to use QSizePolicy to achieve the desired result. The commented lines seem to have no impact on the page at all. I don't know what went wrong.

Cannot create QScrollArea with QWidget and QVBoxLayout to QWidget with QVBoxLayout

I have this code:
class Window(QWidget):
def __init__(self):
super().__init__()
def init_gui(self):
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.new1()
self.new2()
self.showMaximized()
def create_scroll_area(self):
scroll_area = QScrollArea()
widget = QWidget()
scroll_area.setWidget(widget)
layout = QVBoxLayout()
widget.setLayout(layout)
button = QPushButton("Ahoj")
layout.addWidget(button)
self.layout.addLayout(layout)
def new1(self):
self.create_scroll_area()
def new2(self):
self.create_scroll_area()
I get this error message:
QLayout::addChildLayout: layout "" already has a parent
What's wrong?
Who is layout's parent? Widget? I also tried self.widget instead of widget and it still does not work.
Please try this code.
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class Window(QWidget):
def __init__(self, parent=None):
super().__init__(parent=None)
self.init_gui()
def init_gui(self):
self.create_scroll_area()
self.showMaximized()
def create_scroll_area(self):
scroll_area = QScrollArea()
widget = QWidget()
layout = QVBoxLayout()
button = QPushButton("Ahoj")
layout.addWidget(button)
widget.setLayout(layout)
scroll_area.setWidget(widget)
self.setLayout(layout)
def main():
app = QApplication([])
window = Window()
window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Let's revise from A to Z.
1 to write self.init_gui() in __init__ constructor.
If you don't do it, you can't execute init_gui method at the first time.
2.setLayout() or setWidget() should be written at the last place at least.
In python, we prepare the things we want to show, and set them on the mainwidget, and show them at the last time.
3.please pay attention to self.layout name.
Widget has originally setLayout() method. and layout() method.
If you make self.layout = ***, you crush the original method of QWidget.
4. it may as well delete new1 and new2 method.
please call them directly.
5. please look create_scroll_area method.
You make three widget.QScrollArea,QWidget,QPushButton.
and make a layout object.and you set the layout into QWidget.
But you set the QWidget before the widget set the layout.
It is not good order for coding.
You make QPushButton but the button doesn't belong to any widget.
Because you set the button on the self.layout certainly, but if you want to show it, you must setLayout(self.layout) at the last position.

Getting the tops of side-by-side widgets to align using PySide

In the code below, the top of the QTextEdit and QGraphicsView widgets are not aligned when using QHBoxLayout. However, if you comment out QTextEdit and uncomment the other QGraphicsView setup, the top of the widgets align perfectly. Here are my questions:
What causes this alignment issue to occur and how can it be fixed?
Are issues like this best avoided by using Qt Creator?
Is the whole QGraphicsView() --> QGraphicsScene() --> QWidget() necessary to place graphics next to other widgets?
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class Widget(QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__()
# Create Widget1
widget1 = QTextEdit()
#widget1 = QWidget()
#view1 = QGraphicsView()
#scene1 = QGraphicsScene(0,0,200,500)
#view1.setScene(scene1)
#layout = QHBoxLayout()
#layout.addWidget(view1)
#widget1.setLayout(layout)
# Create Widget2
widget2 = QWidget()
view2 = QGraphicsView()
scene2 = QGraphicsScene(0,0,200,500)
view2.setScene(scene2)
layout = QHBoxLayout()
layout.addWidget(view2)
widget2.setLayout(layout)
# Layout of Side by Side windows
container = QWidget()
layout = QHBoxLayout()
layout.addWidget(widget1)
layout.addWidget(widget2)
container.setLayout(layout)
# Scroll Area Properties
scroll = QScrollArea()
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scroll.setWidgetResizable(False)
scroll.setWidget(container)
# Scroll Area Layer add
vLayout = QVBoxLayout(self)
vLayout.addWidget(scroll)
self.setLayout(vLayout)
if __name__ == '__main__':
app = QApplication(sys.argv)
dialog = Widget()
dialog.show()
app.exec_()
The layouts have a default margin. So if one widget is in a layout, and its neighbour is not, they will not be aligned. To remove the default margin, you can do this:
layout = QHBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
However, in your example, the container widget and layout for the QGraphicsView aren't doing anything useful. So you could remove those, and along with some other simplifications, arrive at this:
class Widget(QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__()
widget1 = QTextEdit()
widget2 = QGraphicsView()
widget2.setScene(QGraphicsScene(0, 0, 200, 500, widget2))
container = QWidget()
layout = QHBoxLayout(container)
layout.addWidget(widget1)
layout.addWidget(widget2)
scroll = QScrollArea()
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scroll.setWidgetResizable(False)
scroll.setWidget(container)
vLayout = QVBoxLayout(self)
vLayout.addWidget(scroll)
Using Qt Designer is certainly very useful when experimenting with the layouts of a complex application. However, the code it generates is usually quite verbose compared with what you can achieve when coding by hand. For long-term maintainability, though, using Qt Designer seems the best option.

Pyqt5 updated Widget is not added to the layout though the widget is updated

I am trying to show set of actions when a button is clicked and other set of options if another button is clicked.
Below is the code:
class Screen(QWidget):
def __init__(self):
super(Screen, self).__init__()
layout = QHBoxLayout(self)
self.all_running()
layout.addWidget(self.running_full_widget)
self.actions('1')
layout.addWidget(self.actions_full_widget)
self.setLayout(layout)
self.show()
def all_running(self):
self.running_full_widget = QWidget()
runnning_full_layout= QVBoxLayout()
button1 = QPushButton("btn1")
button2 = QPushButton("btn2")
button1.clicked.connect(lambda: self.actions('2'))
button2.clicked.connect(lambda: self.actions('3'))
runnning_full_layout.addWidget(button1)
runnning_full_layout.addWidget(button2)
self.running_full_widget.setLayout(runnning_full_layout)
def actions(self,value):
self.actions_full_widget= QWidget()
val = int(value)
print(val)
actions_layout = QVBoxLayout()
for i in range(val):
actions_item = QLabel(str(i))
actions_layout.addWidget(actions_item)
self.actions_full_widget.setLayout(actions_layout)
app = QApplication(sys.argv)
Gui = Screen()
sys.exit(app.exec_())
When button is clicked i can see the value is updated but it is not updated in the main layout.
how can i dynamically update the widgets ?
How can I do that in cases where i need to add widgets based on a dynamic value.
did i miss anything with signals and slots?
Kindly correct me if i am wrong. Thanks
Your code was almost there. The problem you are seeing is the widgets were being added but never removed. The following code could be simplified, but I tried to keep it close to yours so you could see the changes more easily.
The main change is now there is a class member screen_layout and the widgets are added/removed from it inside actions().
import sys
from PyQt5.QtWidgets import QHBoxLayout, QVBoxLayout, QWidget, QPushButton, QLabel, QApplication
class Screen(QWidget):
def __init__(self):
super(Screen, self).__init__()
layout = QHBoxLayout(self)
self.screen_layout = layout
self.all_running()
layout.addWidget(self.running_full_widget)
self.actions_full_widget = None
self.actions('1')
# layout.addWidget(self.actions_full_widget)
self.setLayout(layout)
self.show()
def all_running(self):
self.running_full_widget = QWidget()
runnning_full_layout= QVBoxLayout()
button1 = QPushButton("btn1")
button2 = QPushButton("btn2")
button1.clicked.connect(lambda: self.actions('2'))
button2.clicked.connect(lambda: self.actions('3'))
runnning_full_layout.addWidget(button1)
runnning_full_layout.addWidget(button2)
self.running_full_widget.setLayout(runnning_full_layout)
def actions(self,value):
# Remove any previously added widget
if self.actions_full_widget is not None:
self.screen_layout.removeWidget(self.actions_full_widget)
self.actions_full_widget.deleteLater()
self.actions_full_widget= QWidget()
val = int(value)
print(val)
actions_layout = QVBoxLayout()
for i in range(val):
actions_item = QLabel(str(i))
actions_layout.addWidget(actions_item)
self.actions_full_widget.setLayout(actions_layout)
self.screen_layout.addWidget(self.actions_full_widget)
app = QApplication(sys.argv)
Gui = Screen()
sys.exit(app.exec_())

PyQt QVBoxLayout and missing widgets?

I am trying to set up a window that has a text input & a combo box. At the moment I just want to see the text & the selection displayed under the appropriate widget.
I have used QVBoxLayout() as I will be adding more stuff later & thought it would be a simple way of laying out the window.
Unfortunately only the combo box ever gets displayed. The code:
from PyQt4 import QtCore, QtGui
import sys
class Polyhedra(QtGui.QMainWindow):
def __init__(self):
super(Polyhedra, self).__init__()
self.initUI()
def initUI(self):
# Poly names
self.pNames = QtGui.QLabel(self)
polyNameInput = QtGui.QLineEdit(self)
# polyName entry
polyNameInput.textChanged[str].connect(self.onChanged)
# Polytype selection
self.defaultPolyType = QtGui.QLabel("Random polyhedra", self)
polyType = QtGui.QComboBox(self)
polyType.addItem("Random polyhedra")
polyType.addItem("Spheres")
polyType.addItem("Waterman polyhedra")
polyType.activated[str].connect(self.onActivated)
# Layout
vbox = QtGui.QVBoxLayout()
vbox.addWidget(polyNameInput)
vbox.addWidget(self.pNames)
vbox.addWidget(polyType)
vbox.addWidget(self.defaultPolyType)
vbox.addStretch()
# Set up window
self.setGeometry(500, 500, 300, 300)
self.setWindowTitle('Pyticle')
self.show()
# Combo box
def onActivated(self, text):
self.defaultPolyType.setText(text)
self.defaultPolyType.adjustSize()
# Poly names
def onChanged(self, text):
self.pNames.setText(text)
self.pNames.adjustSize()
def main():
app = QtGui.QApplication(sys.argv)
ex = Polyhedra()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
So whats going on here? Am I missing some important directive to QVBoxLayout()?
Using Python 2.7 on Win 7 x64 machine with PyQt 4.
EDIT: Additional problem (still related to missing widgets)
I have amended the code following the clarification below. I then added more widgets when a certain option in the combobox is chosen (see below) but these widgets dont show. I attempted to add a child widget to 'widget' called 'ranPolyWidget' to take a numerical input.
# Combo box
def onActivated(self, text):
if text=="Random polyhedra":
self.randomSeedLbl = QtGui.QLabel("Seed: ", self)
randomSeed = QtGui.QLineEdit(self)
randomSeed.textChanged[str].connect(self.setSeed)
ranPolyWidget = QtGui.QWidget(self.widget)
rbox = QtGui.QVBoxLayout(ranPolyWidget)
rbox.addWidget(randomSeed)
self.layout().addWidget(ranPolyWidget)
self.show()
else:
self.defaultPolyType.setText(text)
self.defaultPolyType.adjustSize()
Same issue as before, no widgets. I am missing something pretty fundamental arent I?
You're forgetting to set it to the widget or main window, so since the QComboBox is the last one made, it's the only one displayed. Basically, everything is added to the layout, but the layout is "free-floating", and so it does not display properly. You need to bind the layout to a QWidget, which I do here. For most widgets, you can can do this by the QtGui.QVBoxLayout(widget) or by widget.setLayout(layout).
Alternatively, if you want multiple layouts on a widget, you can do have a parent layout and then add each child layout to the main layout.
EDIT: This is a better answer:
Make a widget, set layout to widget and set as central widget.
QMainWindow-s don't like you using the builtin layout or overriding it.
widget = QtGui.QWidget()
vbox = QtGui.QVBoxLayout(widget)
self.setCentralWidget(widget)
Old Answer:
self.layout().addLayout(vbox).
This should fix your issue:
Changes I made:
Since QMainWindow already has a layout, add in a widget (28G) and then set the VBoxLayout to the widget and add it to the main window.
from PyQt4 import QtCore, QtGui
import sys
class Polyhedra(QtGui.QMainWindow):
def __init__(self):
super(Polyhedra, self).__init__()
self.initUI()
def initUI(self):
# Poly names
self.pNames = QtGui.QLabel(self)
polyNameInput = QtGui.QLineEdit(self)
# polyName entry
polyNameInput.textChanged[str].connect(self.onChanged)
# Polytype selection
self.defaultPolyType = QtGui.QLabel("Random polyhedra", self)
polyType = QtGui.QComboBox(self)
polyType.addItem("Random polyhedra")
polyType.addItem("Spheres")
polyType.addItem("Waterman polyhedra")
polyType.activated[str].connect(self.onActivated)
# Layout
widget = QtGui.QWidget()
vbox = QtGui.QVBoxLayout(widget)
vbox.addWidget(polyNameInput)
vbox.addWidget(self.pNames)
vbox.addWidget(polyType)
vbox.addWidget(self.defaultPolyType)
vbox.addStretch()
# Set up window
self.setGeometry(500, 500, 300, 300)
self.setWindowTitle('Pyticle')
self.layout().addWidget(widget)
self.show()
# Combo box
def onActivated(self, text):
self.defaultPolyType.setText(text)
self.defaultPolyType.adjustSize()
# Poly names
def onChanged(self, text):
self.pNames.setText(text)
self.pNames.adjustSize()
def main():
app = QtGui.QApplication(sys.argv)
ex = Polyhedra()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
EDIT:
For adding new widgets, you should add them to the layout of the central widget and parent them to that widget.
Here's how I'd restructure your full code:
from PyQt4 import QtCore, QtGui
import sys
class CentralWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(CentralWidget, self).__init__(parent)
# set layouts
self.layout = QtGui.QVBoxLayout(self)
# Poly names
self.pNames = QtGui.QLabel(self)
polyNameInput = QtGui.QLineEdit(self)
# polyName entry
polyNameInput.textChanged[str].connect(self.onChanged)
# Polytype selection
self.defaultPolyType = QtGui.QLabel("Random polyhedra", self)
polyType = QtGui.QComboBox(self)
polyType.addItem("Random polyhedra")
polyType.addItem("Spheres")
polyType.addItem("Waterman polyhedra")
polyType.activated[str].connect(self.onActivated)
self.layout.addWidget(polyNameInput)
self.layout.addWidget(self.pNames)
self.layout.addWidget(polyType)
self.layout.addWidget(self.defaultPolyType)
self.layout.addStretch()
def onActivated(self, text):
'''Adds randSeed to layout'''
if text=="Random polyhedra":
self.randomSeedLbl = QtGui.QLabel("Seed: ", self)
randomSeed = QtGui.QLineEdit(self)
randomSeed.textChanged[str].connect(self.setSeed)
self.layout.addWidget(randomSeed)
else:
self.defaultPolyType.setText(text)
self.defaultPolyType.adjustSize()
# Poly names
def onChanged(self, text):
self.pNames.setText(text)
self.pNames.adjustSize()
class Polyhedra(QtGui.QMainWindow):
def __init__(self):
super(Polyhedra, self).__init__()
# I like having class attributes bound in __init__
# Not very Qt of me, but it's more
# so I break everything down into subclasses
# find it more Pythonic and easier to debug: parent->child
# is easy when I need to repaint or delete child widgets
self.central_widget = CentralWidget(self)
self.setCentralWidget(self.central_widget)
# Set up window
self.setGeometry(500, 500, 300, 300)
self.setWindowTitle('Pyticle')
self.show()
# Combo box
def onActivated(self, text):
'''Pass to child'''
self.central_widget.onActivated(text)
def main():
app = QtGui.QApplication(sys.argv)
ex = Polyhedra()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Categories

Resources