PyQt6/PySide6: QStackedLayout aligns the size of nested widgets - python

I created a QStacedLayout and nested 2 widgets in it.
However, as I understand it, if one widget is larger than the other, then QStackedLayout seems to be trying to equalize the size of the larger one to the smaller one.
Because of this, part of the content of the 2nd widget is "eaten" and appears only after I move the window.
How can this be fixed?
Large Widget Code:
from PySide6.QtWidgets import QFrame, QWidget, QLabel
from PySide6.QtWidgets import QVBoxLayout, QPushButton
from PySide6.QtCore import Qt
import tech
class AboutMe(QFrame):
def __init__(self):
super(AboutMe, self).__init__()
with open(tech.resource_path("ui\\assets\\styles\\AboutMe.qss"), "r", encoding="utf-8") as f:
self.style = f.read()
self.setStyleSheet(self.style)
self.layout = QVBoxLayout()
self.layout.setAlignment(Qt.AlignTop)
self.layout.setContentsMargins(5, 5, 5, 5)
self.layout.setSpacing(3)
self.text = QLabel("text "*800)
self.text.setWordWrap(True)
self.layout.addWidget(self.text)
self.setLayout(self.layout)
class AboutPage(QWidget):
def __init__(self):
super(AboutPage, self).__init__()
self.layout = QVBoxLayout()
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(3)
self.aboutme = AboutMe()
self.layout.addWidget(self.aboutme)
self.setLayout(self.layout)
Main Widget Code:
class MainWidget(QWidget):
def __init__(self, bot, parent):
super(MainWidget, self).__init__()
self.page_layout = QStackedLayout()
self.page_layout.setSpacing(0)
self.page_layout.setContentsMargins(0, 0, 0, 0)
self.main_layout = QVBoxLayout()
self.main_layout.setSpacing(3)
self.main_layout.setContentsMargins(0, 0, 0, 0)
self.main_page = MainPage(bot)
self.about_page = AboutPage()
self.page_layout.addWidget(self.main_page)
self.page_layout.addWidget(self.about_page)
self.page_layout.setCurrentIndex(0)
self.bar = CustomBar(parent)
self.main_layout.addWidget(self.bar)
self.main_layout.addLayout(self.page_layout)
self.setLayout(self.main_layout)
Result:
First Widget:
Second (larger):
When I switched to at least a pixel:

Related

PyQt5 - The "setContentsMargins" method doesn't work properly in a Tab widget (there is a default 9px margin)

I'm writing a GUI with PyQt5 and I realized that the setContentsMargins method doesn't work properly in a Tab widget. Inside of it there is always a 9px margin for each side (see the attached screenshot):
Here is an example (inside the Tab1 I put a TreeView widget with a using the QVBoxLayout):
import sys
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QTabWidget, QVBoxLayout, QTreeView
class App(QMainWindow):
def __init__(self):
super().__init__()
self.title = 'PyQt5 tabs'
self.left = 0
self.top = 0
self.width = 300
self.height = 200
self.setWindowTitle(self.title)
self.setGeometry(self.left, self.top, self.width, self.height)
self.table_widget = MyTableWidget(self)
self.setCentralWidget(self.table_widget)
self.show()
class MyTableWidget(QWidget):
def __init__(self, parent):
super(QWidget, self).__init__(parent)
self.layout = QVBoxLayout(self)
self.tabs = QTabWidget()
self.tab1 = QWidget()
self.tab2 = QWidget()
self.tabs.resize(300,200)
self.tabs.addTab(self.tab1,"Tab 1")
self.tabs.addTab(self.tab2,"Tab 2")
self.tab1.layout = QVBoxLayout(self)
self.TreeView=QTreeView()
self.tab1.layout.addWidget(self.TreeView)
self.tab1.setLayout(self.tab1.layout)
self.tab1.setStyleSheet("background-color: red")
self.tab1.setContentsMargins(-4, -4, -4, -4) # this instruction doesn't work! there is always a default margin (9px for each side)!
self.layout.addWidget(self.tabs)
self.setLayout(self.layout)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
As you can see, if I use the instruction self.tab1.setContentsMargins(10, 10, 10, 10) all the margins, will be 19px (9+10), but what about if a want 5px? The instruction self.tab1.setContentsMargins(-4, -4, -4, -4) doesn't work.
The additional margin is from the widget's layout. Set it to zero or any other value:
self.tab1.layout.setContentsMargins(0, 0, 0, 0)

Call and use a QtabWidget defined in the main class, in another class

I have prepared a little script to illustrate my problem. In fact I would like the button contained in tab 1 (Tab 1) to show me tab 4 (Tab 4). The QTabWidget is contained in the main class (CLASSE_Main) and I'm calling it from class 1 (CLASSE_1). I did some testing but nothing works.
Here is the script:
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Imports PyQt5 -----------------------------------------------------------------------
from PyQt5.QtWidgets import (QWidget, QGroupBox, QGridLayout, QVBoxLayout, QMainWindow,
QTabWidget, QWidget, QApplication, QLabel, QPushButton)
# -------------------------------------------------------------------------------------
import sys
class CLASSE_1(QWidget) :
""" """
def __init__(self, parent=None):
super(CLASSE_1, self).__init__(parent)
label_classe_1 = QLabel("I'm class 1 label")
bouton_affichage_tab_4 = QPushButton("setCurrentIndex Tab 4")
groupe_1 = QGroupBox()
grid_1 = QGridLayout()
grid_1.addWidget(label_classe_1, 0, 0)
grid_1.addWidget(bouton_affichage_tab_4, 0, 1)
groupe_1.setLayout(grid_1)
layout = QGridLayout()
layout.addWidget(groupe_1, 0, 0)
self.setLayout(layout)
# Signal
bouton_affichage_tab_4.clicked.connect(self.show_tab_4)
def show_tab_4(self) :
""" """
from . import CLASSE_Main
cp = CLASSE_Main()
print("dir(cp)", dir(cp))
#cp.tab_widget.setCurrentIndex(3)
print("cp.get_tabwidget(), type(cp.get_tabwidget())", cp.get_tabwidget(), type(cp.get_tabwidget()))
# --------------------------------------
cp.get_tabwidget().setCurrentIndex(3)
# --------------------------------------
for cle, valeur in cp.__dict__.items():
if cle == 'tab_widget' :
print("valeur :", type(valeur))
# --------------------------------------
valeur.setCurrentIndex(3)
# --------------------------------------
class CLASSE_2(QWidget) :
""" """
def __init__(self, parent):
super(CLASSE_2, self).__init__(parent)
label_classe_2 = QLabel("I'm class 2 label")
groupe_1 = QGroupBox()
grid_1 = QGridLayout()
grid_1.addWidget(label_classe_2, 0, 0)
groupe_1.setLayout(grid_1)
layout = QGridLayout()
layout.addWidget(groupe_1, 0, 0)
self.setLayout(layout)
class CLASSE_3(QWidget) :
""" """
def __init__(self, parent=None):
super(CLASSE_3, self).__init__(parent)
label_classe_3 = QLabel("I'm class 3 label")
groupe_1 = QGroupBox()
grid_1 = QGridLayout()
grid_1.addWidget(label_classe_3, 0, 0)
groupe_1.setLayout(grid_1)
layout = QGridLayout()
layout.addWidget(groupe_1, 0, 0)
self.setLayout(layout)
class CLASSE_4(QWidget) :
""" """
def __init__(self, parent):
super(CLASSE_4, self).__init__(parent)
label_classe_4 = QLabel("I'm class 4 label")
groupe_1 = QGroupBox()
grid_1 = QGridLayout()
grid_1.addWidget(label_classe_4, 0, 0)
groupe_1.setLayout(grid_1)
layout = QGridLayout()
layout.addWidget(groupe_1, 0, 0)
self.setLayout(layout)
class CLASSE_5(QWidget) :
""" """
def __init__(self, parent=None):
super(CLASSE_5, self).__init__(parent)
label_classe_5 = QLabel("I'm class 5 label")
groupe_1 = QGroupBox()
grid_1 = QGridLayout()
grid_1.addWidget(label_classe_5, 0, 0)
groupe_1.setLayout(grid_1)
layout = QGridLayout()
layout.addWidget(groupe_1, 0, 0)
self.setLayout(layout)
class CLASSE_6(QWidget) :
""" """
def __init__(self, parent):
super(CLASSE_6, self).__init__(parent)
label_classe_6 = QLabel("I'm class 6 label")
groupe_1 = QGroupBox()
grid_1 = QGridLayout()
grid_1.addWidget(label_classe_6, 0, 0)
groupe_1.setLayout(grid_1)
layout = QGridLayout()
layout.addWidget(groupe_1, 0, 0)
self.setLayout(layout)
class CLASSE_Main(QMainWindow):
""" Main class """
def __init__(self):
super(CLASSE_Main, self).__init__()
self.setWindowTitle('Help me please !')
self.setGeometry(20, 40, 600, 400)
self.setMinimumSize(600, 460)
self.tab_widget = QTabWidget()
self.win_widget_1 = CLASSE_1(self)
self.win_widget_2 = CLASSE_2(self)
self.win_widget_3 = CLASSE_3(self)
self.win_widget_4 = CLASSE_4(self)
self.win_widget_5 = CLASSE_5(self)
self.win_widget_6 = CLASSE_6(self)
widget = QWidget()
layout = QVBoxLayout(widget)
self.tab_widget.addTab(self.win_widget_1, "Tab 1")
self.tab_widget.addTab(self.win_widget_2, "Tab 2")
self.tab_widget.addTab(self.win_widget_3, "Tab 3")
self.tab_widget.addTab(self.win_widget_4, "Tab 4")
self.tab_widget.addTab(self.win_widget_5, "Tab 5")
self.tab_widget.addTab(self.win_widget_6, "Tab 6")
self.tab_widget.setStyleSheet("""QTabWidget::tab-bar {alignment: center;}""")
layout.addWidget(self.tab_widget)
self.setCentralWidget(widget)
def get_tabwidget(self) :
""" """
return self.tab_widget
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = CLASSE_Main()
ex.show()
sys.exit(app.exec_())
Explanation:
It is not necessary to import a class defined in the same script so remove from . import CLASSE_Main.
On the other hand, although the code no longer throws the exception, you have another problem: "cp" is not "ex" since they are 2 different objects.
Solution:
One possible solution is to create a signal that sends the information of the new index that you want to show in the QTabWidget and link that signal to the setCurrentIndex method of the correct QTabWidget:
from PyQt5.QtCore import pyqtSignal
class CLASSE_1(QWidget):
customSignal = pyqtSignal(int)
# ...
def show_tab_4(self):
self.customSignal.emit(3)
class CLASSE_Main(QMainWindow):
""" Main class """
def __init__(self):
super(CLASSE_Main, self).__init__()
# ...
self.setCentralWidget(widget)
self.win_widget_1.customSignal.connect(self.tab_widget.setCurrentIndex)

Scrollarea can't expand (scroll) when new widget added

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

Reusing design elements PyQt

When building a GUI application using python and the qt framework. I would like to reuse certain UI elements without defining them over and over again. In my example I would like to create a drop down menu defining the number of rows with y(number) and result(number)
Application example
import sys
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton,
QLineEdit, QGridLayout, QLabel)
from math import sqrt
class program(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('Program')
flow = QLabel('x1')
pressuredrop_1 = QLabel('y1')
pressuredrop_2 = QLabel('y2 ')
kv = QLabel('result 1')
kv_2 = QLabel('result 2')
grid = QGridLayout()
grid.setSpacing(10)
self.setLayout(grid)
self.setGeometry(300,300,300,150)
self.btn = QPushButton('Calculate', self)
self.flow = QLineEdit(self)
self.pd = QLineEdit(self)
self.result = QLineEdit(self)
self.pd_2 = QLineEdit(self)
self.kvresult_2 = QLineEdit(self)
grid.addWidget(flow, 1,0)
grid.addWidget(self.flow, 1, 1)
grid.addWidget(pressuredrop_1, 2,0)
grid.addWidget(self.pd, 2, 1)
grid.addWidget(pressuredrop_2, 3,0)
grid.addWidget(self.pd_2, 3, 1)
grid.addWidget(kv, 2,2)
grid.addWidget(self.result, 2,3)
grid.addWidget(kv_2, 3,2)
grid.addWidget(self.kvresult_2, 3,3)
grid.addWidget(self.btn, 4,1)
self.btn.clicked.connect(self.calculate)
self.btn.clicked.connect(self.calculate_2)
self.show()

Widget on corner QTabBar is not in corner

I have a pushbutton to add a tab in a QTabWidget. But when I change the button's size, it's not in the corner anymore. So how can I bring it to the corner like in the first picture?
Not change size:
Change size:
Here is my code:
from PyQt4 import QtGui, QtCore
class PlaylistTable(QtGui.QWidget):
def __init__(self):
super(PlaylistTable, self).__init__()
self.playlistTable = QtGui.QTableWidget(self)
self.playlistTable.setFrameShape(QtGui.QFrame.NoFrame)
self.playlistTable.setFrameShadow(QtGui.QFrame.Sunken)
self.playlistTable.setLineWidth(0)
self.playlistTable.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.playlistTable.setVerticalScrollMode(QtGui.QAbstractItemView.ScrollPerPixel)
self.playlistTable.setHorizontalScrollMode(QtGui.QAbstractItemView.ScrollPerPixel)
self.playlistTable.setShowGrid(True)
self.playlistTable.setGridStyle(QtCore.Qt.SolidLine)
self.playlistTable.setWordWrap(True)
self.playlistTable.setCornerButtonEnabled(True)
self.playlistTable.verticalHeader().setVisible(False)
class CustomTabWidget(QtGui.QTabWidget):
"""Tab Widget that that can have new tabs easily added to it."""
def __init__(self, parent=None):
super(CustomTabWidget, self).__init__(parent)
# QtGui.QTabWidget.__init__(self, parent)
# Tab Bar
self.tab = QtGui.QTabBar()
self.setTabBar(self.tab)
# Properties
self.setMovable(True)
self.setTabsClosable(True)
self.plusButton = QtGui.QPushButton("+")
self.plusButton.setFixedSize(QtCore.QSize(22, 22))
self.setCornerWidget(self.plusButton)
# Signals
self.connect(self.plusButton, QtCore.SIGNAL('clicked()'), self.addTab)
# self.tab.plusClicked.connect(self.addTab)
self.tab.tabMoved.connect(self.tab.moveTab)
self.tabCloseRequested.connect(self.removeTab)
def addTab(self):
string = QtCore.QString.fromUtf8("Playlist")
tab = PlaylistTable()
super(CustomTabWidget, self).addTab(tab, string)
class AppDemo(QtGui.QMainWindow):
def __init__(self):
super(AppDemo, self).__init__()
self.centralwidget = QtGui.QWidget(self)
self.horizontalLayout = QtGui.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setContentsMargins(0, -1, 0, -1)
self.playlist_manager = CustomTabWidget(self.centralwidget)
self.horizontalLayout.addWidget(self.playlist_manager)
self.playlist_manager.addTab()
self.setCentralWidget(self.centralwidget)
self.show()
# end class AppDemo
def main():
import sys
app = QtGui.QApplication(sys.argv)
w = AppDemo()
w.setWindowTitle('AppDemo')
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I'm guessing the reason is this, from QTabWidget Documentation:
The geometry of the widget is determined
based on the widget's sizeHint() and the style().
If you print the sizeHint() of the QPushButton you will see that width never goes below a certain value.
I've found an alternative is to use a QToolButton which can do everything (even more) a QPushButton does.
self.plusButton = QtGui.QToolButton(self)
self.plusButton.setText("+")

Categories