How to modify/clear/delete a QWidget from a QGridLayout ? - Python, PyQt - python

I wish to have a QGridLayout containing some informations that can be dynamicaaly changed. However, as it is, it will only superimpose the different informations on top of the previous one instead of replacing it. Surprisingly I couldn't find a method to remove or clean or such a widget from a grid. So what, can't it be dynamically changed ?...
Here is an example of code :
import sys
from PyQt5.QtWidgets import (
QApplication,
QGridLayout,
QPushButton,
QWidget,
QLabel
)
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("QGridLayout Example")
# Create a QGridLayout instance
self.buttonA = QPushButton("A")
self.buttonB = QPushButton("B")
self.layout = QGridLayout()
# Add widgets to the layout
self.layout.addWidget(self.buttonA, 0, 1)
self.layout.addWidget(self.buttonB, 0, 2)
self.layout.addWidget(QPushButton("C"), 1, 0)
self.layout.addWidget(QPushButton("D"), 1, 1)
self.layout.addWidget(QPushButton("E"), 1, 2)
self.layout.addWidget(QPushButton("F"), 2, 0)
self.layout.addWidget(QPushButton("G"), 2, 1)
self.layout.addWidget(QPushButton("H"), 2, 2)
# Set the layout on the application's window
self.setLayout(self.layout)
self.buttonA.clicked.connect(self.change_label)
self.buttonB.clicked.connect(self.change_label2)
def change_label(self):
self.label = QLabel("Hello")
self.layout.addWidget(self.label, 0, 0)
def change_label2(self):
self.label = QLabel("Bonjour")
self.layout.addWidget(self.label, 0, 0)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
When one clicks on button A, I want to have displayed in box (0,0) "Hello". Then if one clicks on button B, I want to replace the text "Hello" with "Bonjour". However I can only have one text on top of the other which is obviously hardly readable then.
Can someone help me ?

Just use this code:
import sys
from PyQt5.QtWidgets import (
QApplication,
QGridLayout,
QPushButton,
QWidget,
QLabel
)
class Window(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("QGridLayout Example")
# Create a QGridLayout instance
self.label = QLabel("Show text here")
self.buttonA = QPushButton("A")
self.buttonB = QPushButton("B")
self.layout = QGridLayout()
# Add widgets to the layout
self.layout.addWidget(self.label, 0, 0)
self.layout.addWidget(self.buttonA, 0, 1)
self.layout.addWidget(self.buttonB, 0, 2)
self.layout.addWidget(QPushButton("C"), 1, 0)
self.layout.addWidget(QPushButton("D"), 1, 1)
self.layout.addWidget(QPushButton("E"), 1, 2)
self.layout.addWidget(QPushButton("F"), 2, 0)
self.layout.addWidget(QPushButton("G"), 2, 1)
self.layout.addWidget(QPushButton("H"), 2, 2)
# Set the layout on the application's window
self.setLayout(self.layout)
self.buttonA.clicked.connect(self.change_label)
self.buttonB.clicked.connect(self.change_label2)
def change_label(self):
self.label.setText("Hello")
def change_label2(self):
self.label.setText("Bonjour")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())

Related

PQYT Make QGridLayout scrollable

I was wondering if it is possible to add a QScrollArea to a QGridLayout? Below is my attempt however, it always fails with the error.
TypeError: setWidget(self, QWidget): argument 1 has unexpected type 'QGridLayout'
Is this method only possible for combo and list boxes? I am basically passing QPixmap images into a QGridLayout which needs to be scrolled. Any help much appreciated thank you.
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class PicClip(QWidget):
def __init__(self):
super().__init__()
# Load image
self.im = QPixmap("1.jpg")
self.im1 = QPixmap("1.jpg")
# Label 1
self.label = QLabel()
self.label.setPixmap(self.im)
# Label 2
self.label1 = QLabel()
self.label1.setPixmap(self.im1)
# Make Grid
self.grid = QGridLayout()
# Create widgets to grid
self.grid.addWidget(self.label, 0, 1, alignment=Qt.AlignCenter)
self.grid.addWidget(self.label1, 1, 1, alignment=Qt.AlignCenter)
# Set layout of Grid
self.setLayout(self.grid)
# Scroll
scroll = QScrollArea()
scroll.setWidget(self.grid)
scroll.setWidgetResizable(True)
# Show
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = PicClip()
sys.exit(app.exec_())
self.grid is layout and you are trying to add layout using setWidget function. setWidget function only gets QWidget
simple trick is to add wrapper QWidget and set self.grid as its layout
import sys
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class PicClip(QMainWindow): # I modified this line to show window properly
def __init__(self):
super().__init__()
# Load image
self.im = QPixmap("1.jpg")
self.im1 = QPixmap("1.jpg")
# Label 1
self.label = QLabel()
self.label.setPixmap(self.im)
# Label 2
self.label1 = QLabel()
self.label1.setPixmap(self.im1)
# Make Grid
self.grid = QGridLayout()
# Create widgets to grid
self.grid.addWidget(self.label, 0, 1, alignment=Qt.AlignCenter)
self.grid.addWidget(self.label1, 1, 1, alignment=Qt.AlignCenter)
# Set layout of Grid
self.setLayout(self.grid)
# Scroll
scroll = QScrollArea()
# add wrapper widget and set its layout
wrapper_widget = QWidget()
wrapper_widget.setLayout(self.grid)
scroll.setWidget(wrapper_widget)
scroll.setWidgetResizable(True)
# Show
self.setCentralWidget(scroll) # I modified this line to show window properly
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = PicClip()
sys.exit(app.exec_())

Python Widget with SizePolicy

Why the sizePolicy doesn't affect on widgets that aren't in layout?
here is an example:
from PyQt5 import QtWidgets
app = QtWidgets.QApplication([])
window = QtWidgets.QWidget()
window.setGeometry(50, 50, 500, 300)
test_widget = QtWidgets.QWidget(window)
test_widget.setMinimumSize(100, 100)
test_widget.setStyleSheet("background-color:red")
size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum)
test_widget.setSizePolicy(size_policy)
window.show()
app.exec()
But that doesn't work, if you changed the main window size the red box still has the same size.
So how can I make that red box resizeable when the parent (main window) is changing.
NOTE: I don't want to use Layouts for some reason.
I'm not sure I completely understand your GUI design but you should note that a single cell (as defined by a row/column pair) in a QGridLayout can be occupied by more than a single widget. A (very) simple example demonstrates this...
#!/usr/local/bin/python3
import os
import sys
from PySide2.QtWidgets import QApplication, QGridLayout, QLabel, QPushButton, QWidget
from PySide2.QtCore import Qt
class widget(QWidget):
def __init__ (self, parent = None):
super(widget, self).__init__(parent)
gl = QGridLayout(self)
pb = QPushButton("Show/Hide Menu")
self.menu = QLabel("Menu goes here...")
self.menu.setAlignment(Qt.AlignCenter)
self.menu.setStyleSheet("background-color: #40800000;")
canvas = QLabel("Canvas")
canvas.setAlignment(Qt.AlignCenter)
canvas.setStyleSheet("background-color: #40000080;")
gl.addWidget(pb, 0, 0)
gl.addWidget(canvas, 0, 0, 2, 2)
pb.raise_()
pb.clicked.connect(self.toggle_menu)
gl.addWidget(self.menu, 1, 0)
self.menu.hide()
def toggle_menu (self, checked):
self.menu.setVisible(not self.menu.isVisible())
if __name__ == '__main__':
app = QApplication([])
w = widget()
w.show()
app.exec_()
[I've used PySide2 as I don't have PyQt5 installed.]
So if I have understood correctly then I don't see why you can't make use of a QGridLayout. Could save you a lot of work.

Why PlotWidget does't scale in GridLayout cell?

I put some widgets in a grid. But all the time there is a strange behavior of these widgets.
If you put one ImageView and PlotWidget near, ImageView cringe and PlotWidged stretched. I put near two ImageView and two PlotWidget to check - everything works fine. What's wrong?
import sys
import numpy as np
from PyQt5.QtWidgets import QWidget, QGridLayout, QPushButton, QApplication
import pyqtgraph as pg
from PIL import Image
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
grid = QGridLayout()
self.setLayout(grid)
self.Imv = pg.ImageView()
win = pg.PlotWidget()
win.plot(np.random.random_sample(40)*10)
btn1 = QPushButton("Start")
btn2 = QPushButton('Forward/backward')
btn3 = QPushButton('Filters')
grid.addWidget(self.Imv, 0, 0, 1, 1)
grid.addWidget(win, 0, 1, 1, 1)
grid.addWidget(btn1, 1, 0, 1, 1)
grid.addWidget(btn2, 2, 0, 1, 1)
grid.addWidget(btn3, 3, 0, 1, 1)
btn1.clicked.connect(self.cap)
self.move(300, 150)
self.setWindowTitle('Scentific camera view')
self.show()
def cap(self):
global Imv
img = np.array(Image.open('D:/Frames/image7.jpg'))
self.Imv.setImage(img.T)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
So it looks:
Two ImageView near - it's fine:
I would recomend to build your UI with QTDesigner and autogenerate the code. That makes things much easier. If you have to do it by hand try to set size policy to expanding or change the geometry to a fixed size:
win.setGeometry(QtCore.QRect(10, 20, 621, 181))
win.setSizePolicy(QtWidgets.QSizePolicy.Expanding)

how to let the layout showed in the bottom

The window would have two layouts: A, the main layout to show some images; B, the qlabel to show some status. When the window is started, A would be empty. A would show some images after some operations.
My question is: how to let B show in the bottom? The current code is:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class TimeLine(QMainWindow):
def __init__(self):
super(TimeLine, self).__init__()
widget = QWidget()
self.setCentralWidget(widget)
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
widget.setLayout(layout)
statusLabel = QLabel('status bar')
statusLabel.setStyleSheet("background-color:blue")
statusLabel.setMaximumHeight(20)
layout.addWidget(statusLabel)
layout.setSpacing(0)
app = QApplication(sys.argv)
window = TimeLine()
window.show()
app.exec_()
And the result is:
The blue label shows in the middle, and I want it to be show in the bottom.
Then, I add a stretch. The layout B would show in the bottom, however, the layout A would also be squeezed. The code is:
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
class TimeLine(QMainWindow):
def __init__(self):
super(TimeLine, self).__init__()
widget = QWidget()
self.setCentralWidget(widget)
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
widget.setLayout(layout)
mainLabel = QLabel()
mainLabel.setStyleSheet("background-color:yellow")
statusLabel = QLabel()
statusLabel.setStyleSheet("background-color:blue")
statusLabel.setMaximumHeight(20)
layout.addWidget(mainLabel)
layout.addStretch()
layout.addWidget(statusLabel)
layout.setSpacing(0)
app = QApplication(sys.argv)
window = TimeLine()
window.show()
app.exec_()
And the result is:
The yellow label is the main label, the gray color represents the back ground. I want the yellow label to occupy all the window except from the blue label.
Thus, what I want is:
when there is no other label, I hope the blue label show in the bottom, like:
where there is another label (yellow label), I hope the yellow label occupy all the space except from the blue label, like:
I saw your task like this:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
class TimeLine(QMainWindow):
def __init__(self):
super(TimeLine, self).__init__()
widget = QWidget()
self.setCentralWidget(widget)
mainLabel = QLabel("This is a label.", alignment = Qt.AlignCenter)
mainLabel.setStyleSheet("background-color:yellow")
layout = QVBoxLayout(widget)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(mainLabel)
self.statusBar().setStyleSheet("background-color:blue; color: #fff")
self.statusBar().showMessage('Hello {} !'.format('status bar'))
if __name__ == "__main__":
app = QApplication(sys.argv)
window = TimeLine()
window.show()
sys.exit(app.exec_())
If I understand your question correctly, you want the blue label to be aligned with the bottom edge of the main window at all times. One way to get what you want to to set the alignment of the label to Qt.AlignBottom when adding it to the layout. You also need to adjust the stretch factors of the two labels to make sure the yellow label takes up all the space above the blue label when it's present, i.e.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
class TimeLine(QMainWindow):
def __init__(self):
super(TimeLine, self).__init__()
widget = QWidget()
self.setCentralWidget(widget)
layout = QVBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
widget.setLayout(layout)
mainLabel = QLabel()
mainLabel.setStyleSheet("background-color:yellow")
statusLabel = QLabel('status bar')
statusLabel.setStyleSheet("background-color:blue")
statusLabel.setMaximumHeight(20)
layout.addWidget(mainLabel, stretch = 1)
layout.addWidget(statusLabel, stretch = 0, alignment = Qt.AlignBottom)
layout.setSpacing(0)
app = QApplication(sys.argv)
window = TimeLine()
window.show()
app.exec_()
Both labels:
Blue label only:

Align QLCDNumber text to right

I have QGridLayout in which I insert QLCDNumber as follows:
self.mainLayout.addWidget(display, 0, 0, 1 , 5)
Everything is quite fine but the digit (0 by default) is 'moving' from center to right while window scaling. Can someone give me a hint how to prevent QLCDNumber from scalling and to align text inside it to the right?
Try it:
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Window(QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.lcd = QLCDNumber()
self.lcd.setFixedSize(150, 70) # Set the size you want
self.lcd.display(12345)
centralWidget = QWidget()
self.setCentralWidget(centralWidget)
self.mainLayout = QGridLayout(centralWidget)
self.mainLayout.addWidget(self.lcd, 0, 0, Qt.AlignRight) # Set the alignment option
self.mainLayout.addWidget(QTextEdit("TextEdit..."), 1,0)
self.mainLayout.addWidget(QPushButton("Button"), 2,0, Qt.AlignCenter)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Window()
ex.show()
sys.exit(app.exec_())

Categories