I have been trying to create a program where QFrames are dynamically added to a QVboxLayout. As more and more frames are added to the layout, the frames not having enough space to occupy. I searched google for the problem, I found many stackoverflow answers, all of which use a QScrollArea. But when I added a QScrollArea with the QVBoxLayout in it, the scrollbars don't show up. Any help would be greatly appreciated. Here is the minimum reproducible example:
from PyQt5 import QtCore, QtGui, QtWidgets
class Frame(QtWidgets.QFrame):
def __init__(self,parent=None):
super(Frame,self).__init__(parent)
self.setStyleSheet("background-color:red")
self.lbl=QtWidgets.QLabel(self)
self.lbl.setText("Sample Test")
self.font=QtGui.QFont()
self.font.setPointSize(20)
self.lbl.setFont(self.font)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.setFixedSize(984, 641)
MainWindow.setStyleSheet("background-color:rgb(255,255,255);")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
#Some UI
self.addBtn = QtWidgets.QPushButton(self.centralwidget)
self.addBtn.setGeometry(QtCore.QRect(450, 100, 71, 51))
self.addBtn.setText("+")
font = QtGui.QFont()
font.setPointSize(20)
self.addBtn.setFont(font)
self.addBtn.setStyleSheet("background-color:rgb(89, 183, 255);border-radius:15px;color:white;")
self.addBtn.setFlat(True)
self.addBtn.setObjectName("addTaskBtn")
self.addBtn.clicked.connect(self.addFrame)
self.scroller = QtWidgets.QScrollArea(self.centralwidget)
self.scroller.setGeometry(QtCore.QRect(0, 230, 991, 411))
self.scroller.setWidgetResizable(True)
self.scroller.setObjectName("scroller")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 989, 409))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.layoutManager=QtWidgets.QVBoxLayout(self.scroller)
self.scrollAreaWidgetContents.setLayout(self.layoutManager)
self.scroller.setWidget(self.scrollAreaWidgetContents)
MainWindow.setCentralWidget(self.centralwidget)
def addFrame(self):
#Code to add the frame
self.layoutManager.addWidget(Frame())
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
The problem is that when you use the widgetResizable mode in True it uses the sizeHint of the widget but in your case the sizeHint of the QFrame does not take into account that of the QLabel. One possible solution is to use a layout.
class Frame(QtWidgets.QFrame):
def __init__(self, parent=None):
super(Frame, self).__init__(parent)
self.setStyleSheet("background-color:red")
self.lbl = QtWidgets.QLabel()
self.lbl.setText("Sample Test")
font = QtGui.QFont()
font.setPointSize(20)
self.lbl.setFont(font)
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.lbl)
On the other hand I see that in my test the scrollbar appears but only a part since as you established it using setGeometry it has problems to recalculate some values. A possible solution is to use a widget that contains the QScrollArea:
# ...
self.addBtn.clicked.connect(self.addFrame)
container = QtWidgets.QWidget(self.centralwidget)
container.setGeometry(QtCore.QRect(0, 230, 991, 411))
lay_container = QtWidgets.QVBoxLayout(container)
self.scroller = QtWidgets.QScrollArea()
lay_container.addWidget(self.scroller)
self.scroller.setWidgetResizable(True)
# ...
Related
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'PyosUI.ui'
#
# Created by: PyQt5 UI code generator 5.15.7
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import (
QApplication,
QLabel,
QMainWindow,
QPushButton,
QVBoxLayout,
QWidget
)
class GameWindow(QWidget):
def __init__(self):
super.__init__()
layout = QVBoxLayout()
self.label = QLabel("Hello!")
layout.addWidget(self.label)
self.setLayout(layout)
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.setEnabled(True)
Dialog.resize(1044, 601)
self.Notepad = QtWidgets.QPushButton(Dialog)
self.Notepad.setGeometry(QtCore.QRect(50, 520, 281, 61))
self.Notepad.setObjectName("Notepad")
self.Game = QtWidgets.QPushButton(Dialog)
self.Game.setGeometry(QtCore.QRect(390, 520, 271, 61))
self.Game.setObjectName("Game")
self.Poweroff = QtWidgets.QPushButton(Dialog)
self.Poweroff.setGeometry(QtCore.QRect(690, 520, 111, 31))
self.Poweroff.setObjectName("Poweroff")
self.Settings = QtWidgets.QPushButton(Dialog)
self.Settings.setGeometry(QtCore.QRect(690, 560, 111, 31))
self.Settings.setObjectName("Settings")
self.Date_time = QtWidgets.QDateTimeEdit(Dialog)
self.Date_time.setGeometry(QtCore.QRect(840, 20, 194, 22))
self.Date_time.setObjectName("Date_time")
self.label = QtWidgets.QLabel(Dialog)
self.label.setGeometry(QtCore.QRect(0, -20, 1051, 641))
self.label.setText("")
self.label.setPixmap(QtGui.QPixmap("background.png"))
self.label.setObjectName("label")
self.label.raise_()
self.Notepad.raise_()
self.Game.raise_()
self.Poweroff.raise_()
self.Settings.raise_()
self.Date_time.raise_()
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
l = QVBoxLayout()
self.Game.clicked.connect(self.button_clicked)
l.addWidget(self.Game)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "PyOS"))
self.Notepad.setText(_translate("Dialog", "Notepad"))
self.Game.setText(_translate("Dialog", "Game"))
self.Poweroff.setText(_translate("Dialog", "Poweroff"))
self.Settings.setText(_translate("Dialog", "Settings"))
def game_window(self):
game_window = QtWidgets.QDialog()
game_window.setWindowTitle("Game")
game_window.resize(600, 600)
game_window.show()
def button_clicked(self):
self.game_window()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Dialog = QtWidgets.QDialog()
ui = Ui_Dialog()
ui.setupUi(Dialog)
Dialog.show()
sys.exit(app.exec_())
The above code is the UI I made with Pyqt5 designer. For the work I want to make, when the 'Game' button is clicked, a window dedicated to the 'Game' button should appear.
However, when I run the code above, when I press the 'Game' button, a 600 x 600 window appears and then disappears.
Why?
How else can I solve this problem?
Thank you.
The game_window is a local variable within the game_window() method in Ui_Dialog.
Set the window as an instance and it should be fine.
Modified method
# other code remains same.
def game_window(self):
self._game_window = QtWidgets.QDialog()
self._game_window.setWindowTitle("Game")
self._game_window.resize(600, 600)
self._game_window.show()
Edit: The reason to add the underscore (_) is to avoid having the same name as the method name.
Say i have a layout like this:
I want the ratio of the left object and the right object to always be like this:
So that i can add a small image in the top left corner that wont be gigantic or too small, but always in nice relation to the size of the window. How can i do this, either in the designer, or in the code?
I already found that you seem to be able to do this via selecting the layout and changing the LayoutStretch to something like 1,3 - this worked in the designer, however, when i inserted my image in the code, it did not respect it and blew the layout out of proption again.
I added a stretcher and used a QLabel to display the image, and then added the file via self.LogoLabel.setPixmap(QtGui.QPixmap('res/image.png')) , so i do not understand what i need to change in order to get the image to always be nice and small in the top left corner.
A test example, in case the question wasnt clear enough - the image i need is 1000x710px large.
from PyQt5 import QtCore, QtWidgets, QtGui
import sys
class Ui_ZEBRA(object):
def setupUi(self, ZEBRA):
ZEBRA.setObjectName("ZEBRA")
ZEBRA.resize(315, 134)
self.centralwidget = QtWidgets.QWidget(ZEBRA)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setContentsMargins(-1, -1, -1, 0)
self.horizontalLayout.setSpacing(0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.verticalLayout_3 = QtWidgets.QVBoxLayout()
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.LogoLabel = QtWidgets.QLabel(self.centralwidget)
self.LogoLabel.setText("")
self.LogoLabel.setScaledContents(True)
self.LogoLabel.setObjectName("LogoLabel")
self.verticalLayout_3.addWidget(self.LogoLabel)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_3.addItem(spacerItem)
self.ComboBox_InputType = QtWidgets.QComboBox(self.centralwidget)
self.ComboBox_InputType.setObjectName("ComboBox_InputType")
self.ComboBox_InputType.addItem("")
self.ComboBox_InputType.addItem("")
self.ComboBox_InputType.addItem("")
self.ComboBox_InputType.addItem("")
self.verticalLayout_3.addWidget(self.ComboBox_InputType)
self.horizontalLayout.addLayout(self.verticalLayout_3)
self.TextInput_Devices = QtWidgets.QPlainTextEdit(self.centralwidget)
self.TextInput_Devices.setObjectName("TextInput_Devices")
self.horizontalLayout.addWidget(self.TextInput_Devices)
self.horizontalLayout.setStretch(0, 1)
self.horizontalLayout.setStretch(1, 3)
self.verticalLayout_4.addLayout(self.horizontalLayout)
ZEBRA.setCentralWidget(self.centralwidget)
self.menuBar_EnterToken = QtWidgets.QAction(ZEBRA)
self.menuBar_EnterToken.setObjectName("menuBar_EnterToken")
self.menuBar_TestToken = QtWidgets.QAction(ZEBRA)
self.menuBar_TestToken.setObjectName("menuBar_TestToken")
self.menuBar_About = QtWidgets.QAction(ZEBRA)
self.menuBar_About.setObjectName("menuBar_About")
self.retranslateUi(ZEBRA)
QtCore.QMetaObject.connectSlotsByName(ZEBRA)
def retranslateUi(self, ZEBRA):
_translate = QtCore.QCoreApplication.translate
ZEBRA.setWindowTitle(_translate("ZEBRA", "ZEBRA"))
self.ComboBox_InputType.setItemText(0, _translate("ZEBRA", "ip"))
self.ComboBox_InputType.setItemText(1, _translate("ZEBRA", "Use all devices"))
self.ComboBox_InputType.setItemText(2, _translate("ZEBRA", "displayName"))
self.ComboBox_InputType.setItemText(3, _translate("ZEBRA", "id"))
self.menuBar_EnterToken.setText(_translate("ZEBRA", "Enter Accesstoken"))
self.menuBar_TestToken.setText(_translate("ZEBRA", "Test Accesstoken"))
self.menuBar_About.setText(_translate("ZEBRA", "About..."))
class Test(QtWidgets.QMainWindow, Ui_ZEBRA):
def __init__(self, parent=None):
super(Test, self).__init__(parent)
self.setupUi(self)
self.LogoLabel.setPixmap(QtGui.QPixmap('res/image.png'))
def main():
app = QtWidgets.QApplication(sys.argv)
form = Test()
form.show()
app.exec_()
if __name__ == '__main__':
main()
EDIT: Weirdly enough, i could not find a single working example on how to use an image in a QLabel and scale its size while changing the window size, while also keeping the aspect ratio. Such a basic thing that is nowhere to be found?
You firstly need to change the layout so that it uses alignment rather than expanding spacers to keep the label at the top left corner. Also, some properties of the label need adjustment so that it can resize itself freely. This can all be done in Qt Designer, but your example code can also be fixed manually like this:
self.LogoLabel = QtWidgets.QLabel(self.centralwidget)
self.LogoLabel.setText("")
self.LogoLabel.setObjectName("LogoLabel")
# new stuff
self.LogoLabel.setScaledContents(False)
self.LogoLabel.setMinimumSize(1, 1)
self.LogoLabel.setSizePolicy(
QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.LogoLabel.setAlignment(QtCore.Qt.AlignTop)
self.verticalLayout_3.setAlignment(QtCore.Qt.AlignTop)
self.verticalLayout_3.addWidget(self.LogoLabel)
The dynamic resizing can then be handled using an event-filter, like this:
class Test(QtWidgets.QMainWindow, Ui_ZEBRA):
def __init__(self, parent=None):
super(Test, self).__init__(parent)
self.setupUi(self)
self._logo = QtGui.QPixmap('res/image.png')
self.LogoLabel.setPixmap(self._logo)
self.LogoLabel.installEventFilter(self)
def eventFilter(self, widget, event):
if event.type() == QtCore.QEvent.Resize and widget is self.LogoLabel:
self.LogoLabel.setPixmap(self._logo.scaled(
self.LogoLabel.width(), self.LogoLabel.height(),
QtCore.Qt.KeepAspectRatio))
return True
return super(Test, self).eventFilter(widget, event)
To scale the image down as you wanted and keep the ratio between the two elements, you need to set setScaledContents(True) like this:
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(559, 289)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.label = QtWidgets.QLabel(self.centralwidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
self.label.setSizePolicy(sizePolicy)
self.label.setScaledContents(True)
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setObjectName("label")
self.verticalLayout.addWidget(self.label)
self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.comboBox.setObjectName("comboBox")
self.verticalLayout.addWidget(self.comboBox)
self.horizontalLayout.addLayout(self.verticalLayout)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
self.textBrowser.setObjectName("textBrowser")
self.horizontalLayout.addWidget(self.textBrowser)
self.horizontalLayout.setStretch(0, 1)
self.horizontalLayout.setStretch(2, 3)
self.verticalLayout_2.addLayout(self.horizontalLayout)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.label.setText(_translate("MainWindow", "TextLabel"))
import sys
class Test(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(Test, self).__init__(parent)
self.setupUi(self)
pixmap = QtGui.QPixmap('res/image.png')
pixmap = pixmap.scaled(256, 128, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
self.label.setPixmap(pixmap)
self.label.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)
def main():
app = QtWidgets.QApplication(sys.argv)
form = Test()
form.show()
app.exec_()
if __name__ == '__main__':
main()
In order to keep the aspect ratio - i think you have to use a resizeEvent of the Widget containing the Label which changes the size to the correct aspect ratio whenever the event is triggered.
So I just started working with PyQt5. Right now I only programmed 1 button that's supposed to take text from a line edit, store it in a global variable and put it in a text browser. Now it does this... but with issues.
The text browser does NOT update until I click another program/window and then click my app again
When the line edit is cleared there is a bug which is basically text not being cleared properly but only it's top half. This goes away when I type again.
I tried calling the .update() methods for the widgets and QApplication.process_events()
Here's the code
from PyQt5 import QtCore, QtGui, QtWidgets
lyrics = ''
adlib = ' (Placeholder adlib)'
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(742, 680)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.addLineBtn = QtWidgets.QPushButton(self.centralwidget)
self.addLineBtn.setGeometry(QtCore.QRect(530, 0, 91, 51))
font = QtGui.QFont()
font.setFamily("Arial")
font.setItalic(True)
self.addLineBtn.setFont(font)
self.addLineBtn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
self.addLineBtn.setObjectName("addLineBtn")
self.deleteBtn = QtWidgets.QPushButton(self.centralwidget)
self.deleteBtn.setGeometry(QtCore.QRect(120, 80, 91, 32))
self.deleteBtn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
self.deleteBtn.setObjectName("deleteBtn")
self.saveBtn = QtWidgets.QPushButton(self.centralwidget)
self.saveBtn.setGeometry(QtCore.QRect(30, 80, 91, 32))
self.saveBtn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
self.saveBtn.setObjectName("saveBtn")
self.lineEdit = QtWidgets.QLineEdit(self.centralwidget)
self.lineEdit.setGeometry(QtCore.QRect(30, 20, 501, 51))
self.lineEdit.setObjectName("lineEdit")
self.dialLabel = QtWidgets.QLabel(self.centralwidget)
self.dialLabel.setGeometry(QtCore.QRect(640, 20, 71, 16))
self.dialLabel.setObjectName("dialLabel")
self.rtdSlider = QtWidgets.QSlider(self.centralwidget)
self.rtdSlider.setGeometry(QtCore.QRect(620, 40, 101, 22))
self.rtdSlider.setOrientation(QtCore.Qt.Horizontal)
self.rtdSlider.setObjectName("rtdSlider")
self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
self.textBrowser.setGeometry(QtCore.QRect(20, 120, 701, 501))
self.textBrowser.setObjectName("textBrowser")
self.noadlibBtn = QtWidgets.QPushButton(self.centralwidget)
self.noadlibBtn.setGeometry(QtCore.QRect(530, 50, 91, 51))
font = QtGui.QFont()
font.setFamily("Arial")
font.setItalic(True)
self.noadlibBtn.setFont(font)
self.noadlibBtn.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
self.noadlibBtn.setObjectName("noadlibBtn")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 742, 22))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
self.addLineBtn.clicked.connect(self.addLineAdlib)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.addLineBtn.setText(_translate("MainWindow", "Adlib"))
self.deleteBtn.setText(_translate("MainWindow", "Delete"))
self.saveBtn.setText(_translate("MainWindow", "Save"))
self.dialLabel.setText(_translate("MainWindow", "RTD Level"))
self.noadlibBtn.setText(_translate("MainWindow", "No Adlib"))
def addLineAdlib(self):
global lyrics
lyrics += self.lineEdit.text() + adlib + '\n'
self.lineEdit.clear()
self.textBrowser.setText(lyrics)
def addLineNoAdlib(self):
pass
def save(self):
pass
def deleteLine(self):
pass
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
I found the same problem as yours and I use MacOS Mojave 10.14.6. This problem maybe due to OS, but I downgraded pyqt5 from the latest 5.14.1 to a stable version 5.9.2, the problem was solved.
This problem also happens with macos 10.15.6 + pyqt 5.15.0.
I created a small example with qtdesigner (with 1 lineEdit box, 1 label box, 1 button), and then created python file with pyuic5 as well as with pyside2-uic.
Both the generated python files have the same problem - when "click" is pressed I have to move out of the window and back again to see the results. But if I just press enter after the input text then it shows up immediately.
The problem is solved by adding an extra line in the main class, for repainting the GUI, as shown below:
import sys
from PyQt5.QtWidgets import QDialog, QApplication
from demo_ui_pyuic5 import *
class MyForm(QDialog):
def __init__(self):
super().__init__()
self.ui = Ui_Dialog()
self.ui.setupUi(self)
self.ui.buttonClick.clicked.connect(self.dispmessage)
self.show()
def dispmessage(self):
self.ui.labelResponse.setText("Hello " + self.ui.lineEditName.text())
self.ui.labelResponse.repaint() # this line added to fix the problem
if __name__=="__main__":
app = QApplication(sys.argv)
w = MyForm()
w.show()
sys.exit(app.exec_())
1. The text browser does NOT update until I click another program/window and then click my app again - this issue can be related with your OS. I have got incorrect PyQT5 behavior on Ubuntu 16.04 while on Ubuntu 18.04 everything was working fine. Can you share more info?
Note: I tested your script on Ubuntu 18.04, I didn't need to click another program/window to refresh it
2. When the line edit is cleared there is a bug which is basically text not being cleared properly but only it's top half. This goes away when I type again. - Can you explain what do you mean by it's top half? If you meant that there is still (Placeholder adlib) text, you should modify your function to check lineEdit textbox before adding something to lyrics variable. For example, like this:
def addLineAdlib(self):
if self.lineEdit.text():
global lyrics
lyrics += self.lineEdit.text() + adlib + '\n'
self.lineEdit.clear()
self.textBrowser.setText(lyrics)
Here is a cleaner more class-ified (more pythonic pyqt) version of your program I have left commented out stuff in for you to see where it would have been but I removed your coordinate system and replaced it with the more standard Layout style of creating a GUI.
I tested this using python 3.7 pyqt5 on win10 so if there is OS issue you will know it but my guess (without digging into that ugly mess) is that there was a disconnection some where which created the issue you were experiencing.
Also your button as designed will never create a blank line since its always putting something on a line -- I tested this by just clicking the button without entering anything
from sys import exit as sysExit
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont, QCursor
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QHBoxLayout, QVBoxLayout, QDockWidget, QStyleFactory
from PyQt5.QtWidgets import QPushButton, QLineEdit, QLabel, QSlider, QTextBrowser, QMenuBar, QStatusBar
class MenuToolBar(QDockWidget):
def __init__(self, MainWin):
QDockWidget.__init__(self)
self.MainWin = MainWin
self.MainMenu = MainWin.menuBar()
# ******* Create the Help Menu *******
self.HelpMenu = self.MainMenu.addMenu('Help')
class CenterPanel(QWidget):
def __init__(self, parent):
QWidget.__init__(self)
# General Font Object for a couple of Buttons
btnFont = QFont()
btnFont.setFamily('Arial')
btnFont.setItalic(True)
# First Item Horizontal Box 1 Containing the AddLib Entry and Button
self.lnAdlibEntry = QLineEdit(self)
# self.lnAdlibEntry.setGeometry(QRect(30, 20, 501, 51))
# self.lnAdlibEntry.resize(501, 51)
self.btnAddLine = QPushButton(self)
# Left, Top, Width, Height
# self.btnAddLine.setGeometry(QRect(530, 0, 91, 51))
# Width, Height
# self.btnAddLine.resize(91, 51)
self.btnAddLine.setFont(btnFont)
self.btnAddLine.setCursor(QCursor(Qt.PointingHandCursor))
self.btnAddLine.setText('Adlib')
self.btnAddLine.clicked.connect(parent.AddLineAdlib)
HBox1 = QHBoxLayout()
HBox1.addWidget(self.lnAdlibEntry)
HBox1.addWidget(self.btnAddLine)
# Second Item Vertical Box 1 Containing the AdlibEntry LineEdit and RTD Label and RTD Slider
self.lblDial = QLabel(self)
# self.lblDial.setGeometry(QRect(640, 20, 71, 16))
# self.lblDial.resize(71, 16)
self.lblDial.setText("RTD Level")
self.sldrRtd = QSlider(self)
# self.sldrRtd.setGeometry(QRect(620, 40, 101, 22))
# self.sldrRtd.resize(101, 22)
self.sldrRtd.setOrientation(Qt.Horizontal)
VBox1 = QVBoxLayout()
VBox1.addWidget(self.lblDial)
VBox1.addWidget(self.sldrRtd)
# Third Item Horizontal Box 2 containing the Save, No Adlib and Delete buttons
self.btnNoAdlib = QPushButton(self)
# self.btnNoAdlib.setGeometry(QRect(530, 50, 91, 51))
# self.btnNoAdlib.resize(91, 51)
self.btnNoAdlib.setFont(btnFont)
self.btnNoAdlib.setCursor(QCursor(Qt.PointingHandCursor))
self.btnNoAdlib.setText("No Adlib")
self.btnSave = QPushButton(self)
# self.btnSave.setGeometry(QRect(30, 80, 91, 32))
# self.btnSave.resize(91, 32)
self.btnSave.setCursor(QCursor(Qt.PointingHandCursor))
self.btnSave.setText('Save')
self.btnDelete = QPushButton(self)
# self.btnDelete.setGeometry(QRect(120, 80, 91, 32))
# self.btnDelete.resize(91, 32)
self.btnDelete.setCursor(QCursor(Qt.PointingHandCursor))
self.btnDelete.setText('Delete')
HBox2 = QHBoxLayout()
HBox2.addWidget(self.btnSave)
HBox2.addStretch(1)
HBox2.addWidget(self.btnNoAdlib)
HBox2.addStretch(1)
HBox2.addWidget(self.btnDelete)
# Sixth Item Text Browser
self.txtBrowser = QTextBrowser(self)
# self.txtBrowser.setGeometry(QRect(20, 120, 701, 501))
# self.txtBrowser.resize(701, 501)
VBox2 = QVBoxLayout()
VBox2.addLayout(HBox1)
VBox2.addLayout(VBox1)
VBox2.addLayout(HBox2)
VBox2.addWidget(self.txtBrowser)
self.setLayout(VBox2)
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.Lyrics = ''
self.Adlib = ' (Placeholder adlib)'
self.setWindowTitle('Main Window')
self.resize(742, 680)
self.CenterPane = CenterPanel(self)
self.setCentralWidget(self.CenterPane)
self.MenuBar = MenuToolBar(self)
# self.MenuBar.setGeometry(QRect(0, 0, 742, 22))
self.SetStatusBar(self)
self.setStyle(QStyleFactory.create('Cleanlooks'))
def SetStatusBar(self, parent):
StatusMsg = ''
parent.StatBar = parent.statusBar()
if len(StatusMsg) < 1:
StatusMsg = 'Ready'
parent.StatBar.showMessage(StatusMsg)
def AddLineAdlib(self):
# This statement retains everything previously in Lyrics and
# everything in the AdlibEntry box and everything in Adlib which
# I am not sure is what you are wanting but it is what you had
self.Lyrics += self.CenterPane.lnAdlibEntry.text() + self.Adlib + '\n'
self.CenterPane.lnAdlibEntry.clear()
self.CenterPane.txtBrowser.setText(self.Lyrics)
def AddLineNoAdlib(self):
pass
def Save(self):
pass
def DeleteLine(self):
pass
if __name__ == "__main__":
MainThred = QApplication([])
MainGUI = MainWindow()
MainGUI.show()
sysExit(MainThred.exec_())
Edited: I had an odd arrow appearing on my screen which it appears the QStyleFactory call removes and I adjust the StatusBar declaration to be more modular in case you want to Class-ify it later
I have the problem that completely drives me mad.
I am writing GUI application on Python using PyQt5. My application consists of multiple QGroupBoxes, that become visible and non-visible as user switches between them.
One of QGroupBoxes contains QScrollArea, in which another QGroupBoxes are placed. As user adds information to application, new QGroupBoxes might be added, so QScrollArea should allow to view all of them when there are too much elements added.
So the structure of elements is:
QGroupBox
=>QScrollArea
=>=>QScrollAreaWidgetContents
=>=>=>QVBoxLayout
=>=>=>=>QGroupBox
=>=>=>=>=>QFormLayout
=>=>=>=>QGroupBox
=>=>=>=>=>QFormLayout
However, even though I placed inner QGroupBoxes inside a vertical layout and then inside a single QScrollAreaWidgetContents, QScrollArea does not show any scrollbars, but instead resizes inner elements, so it looks like this.
My problem can be summed up in this example:
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(415, 213)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
self.groupBox.setGeometry(QtCore.QRect(0, 0, 801, 601))
self.groupBox.setObjectName("groupBox")
self.scrollArea = QtWidgets.QScrollArea(self.groupBox)
self.scrollArea.move(10, 30)
self.scrollArea.setFixedWidth(380)
self.scrollArea.setMinimumHeight(160)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setObjectName("scrollArea")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.move(0, 0)
self.scrollAreaWidgetContents.setFixedWidth(378)
self.scrollAreaWidgetContents.setMinimumHeight(158)
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
MainWindow.setCentralWidget(self.centralwidget)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
class competencyBox(QWidget):
def __init__(self, parent):
super(competencyBox, self).__init__(parent)
self.compCodeLineEdit = QLineEdit()
self.compDescrpTextEdit = QTextEdit()
self.box = QGroupBox(self)
self.form_lay = QFormLayout(self)
self.form_lay.addRow(QLabel("Код: "), self.compCodeLineEdit)
self.form_lay.addRow(QLabel("Описание: "), self.compDescrpTextEdit)
self.box.setLayout(self.form_lay)
self.box.setFixedSize(510, 240)
class test_window(QMainWindow):
def __init__(self):
super(test_window, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.addBox(self.ui.scrollAreaWidgetContents, competencyBox, 4)
def addBox(self, parent, element, number):
vert_lay = QVBoxLayout(parent)
for i in range(number):
e = element(parent)
vert_lay.addWidget(e)
vert_lay.setSpacing(5)
As you may notice, I tried different approaches, such as setting fixed size to inner QGroupBoxes, adding spacing into the vertical layout and so on, but QScrollArea still ignores them and shrinks inner elements. I am stuck and got no idea how to solve my problem. Please help me.
The main problem in your case is that the scrollAreaWidgetContents should not have a fixed size since it is the container of the widgets and if you use self.scrollAreaWidgetContents.setFixedWidth (378) you are setting it, the size of the scrollAreaWidgetContents should be the set size. widgets through the QVBoxLayout.
Another problem is that CompetencyBox must use a layout to set up the QGroupBox.
from PyQt5 import QtCore, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(415, 213)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.groupBox = QtWidgets.QGroupBox(self.centralwidget)
self.groupBox.setGeometry(QtCore.QRect(0, 0, 801, 601))
self.groupBox.setObjectName("groupBox")
self.scrollArea = QtWidgets.QScrollArea(self.groupBox)
self.scrollArea.move(10, 30)
self.scrollArea.setFixedWidth(380)
self.scrollArea.setMinimumHeight(160)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setObjectName("scrollArea")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
MainWindow.setCentralWidget(self.centralwidget)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
class CompetencyBox(QtWidgets.QWidget):
def __init__(self, parent=None):
super(CompetencyBox, self).__init__(parent)
self.compCodeLineEdit = QtWidgets.QLineEdit()
self.compDescrpTextEdit = QtWidgets.QTextEdit()
lay = QtWidgets.QVBoxLayout(self)
box = QtWidgets.QGroupBox()
lay.addWidget(box)
form_lay = QtWidgets.QFormLayout()
form_lay.addRow(QtWidgets.QLabel("Код: "), self.compCodeLineEdit)
form_lay.addRow(QtWidgets.QLabel("Описание: "), self.compDescrpTextEdit)
box.setLayout(form_lay)
box.setFixedSize(510, 240)
class Test_Window(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(Test_Window, self).__init__(parent)
self.setupUi(self)
self.addBox(self.scrollAreaWidgetContents, CompetencyBox, 4)
def addBox(self, parent, element, number):
vert_lay = QtWidgets.QVBoxLayout(parent)
for i in range(number):
vert_lay.addWidget(element())
vert_lay.setSpacing(5)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = Test_Window()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())
As the window changes its size, I want to the bottom of the row, which is a QSplitter including 3 widgets, to expand and fill the remaining window while the widgets in the first row keep the original y-position. How to do that?
If the three widgets in QSplitter can keep their former ratio, it'll be better.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
self.MainWindow=MainWindow
self.MainWindow.setObjectName("self.MainWindow")
self.MainWindow.resize(850, 800)
self.centralwidget = QtWidgets.QWidget(self.MainWindow)
self.MainWindow.setCentralWidget(self.centralwidget)
self.vlayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setContentsMargins(0, 0, 0, 0)
self.label = QtWidgets.QLabel(self.centralwidget)
self.label.setText("LABEL")
self.comboBox = QtWidgets.QComboBox(self.centralwidget)
self.gridLayout.addWidget(self.label,0,0,1,1)
self.gridLayout.addWidget(self.comboBox,0,1,1,9)
"""table """
self.tableWidget = QtWidgets.QTableWidget(self.centralwidget)
"""tab1"""
self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
self.tabWidget.setMinimumHeight(50)
"""tab2"""
self.tabWidget_2 = QtWidgets.QTabWidget(self.centralwidget)
self.tabWidget_2.setMinimumHeight(50)
"""splitter window"""
splitter = QtWidgets.QSplitter(QtCore.Qt.Vertical)
splitter.addWidget(self.tableWidget)
splitter.addWidget(self.tabWidget)
splitter.addWidget(self.tabWidget_2)
splitter.setSizes([232,225,225])
self.gridLayout.addWidget(splitter,3,0,5,10)
self.gridLayout.setRowMinimumHeight(3,690)
self.vlayout.addLayout(self.gridLayout)
spacerItem = QtWidgets.QSpacerItem(20, 245, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.vlayout.addItem(spacerItem)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec_())
I want to the QSplitter to expand to the bottom of the window.