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

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.

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.

pyqt5: some widgets are disappear in my label

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)
# ...

PyQt5 QScrollArea widget with dynamically created GroupBoxes

I'm trying to make a toolbox widget that will do various different things. But I'm having trouble with the layout management regarding the QScrollArea. Following the stripped version of the code I have:
from PyQt5 import QtWidgets
import sys
class MyScrollWidget(QtWidgets.QWidget):
def __init__(self):
super(MyScrollWidget, self).__init__()
scrollArea = QtWidgets.QScrollArea(self)
top_widget = QtWidgets.QWidget()
top_layout = QtWidgets.QVBoxLayout()
for i in range(10):
group_box = QtWidgets.QGroupBox()
group_box.setTitle('GroupBox For Item {0}'.format(i))
layout = QtWidgets.QHBoxLayout(group_box)
label = QtWidgets.QLabel()
label.setText('Label For Item {0}'.format(i))
layout.addWidget(label)
push_button = QtWidgets.QPushButton(group_box)
push_button.setText('Run Button')
push_button.setFixedSize(100, 32)
layout.addWidget(push_button)
group_box.setLayout(layout)
top_layout.addWidget(group_box)
top_widget.setLayout(top_layout)
scrollArea.setWidget(top_widget)
self.resize(200, 500)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
widget = MyScrollWidget()
widget.show()
sys.exit(app.exec_())
But this only gives me a small fixed subsection of the widget that scrolled. But what I really wants is the whole widget to be scrollable if the widget/window is smaller than the total size of all the group boxes. I.e I'd like the widget to be used as if it was all fixed width, but if the user resized the widget smaller than that, it would scroll appropriately. I've tried various different methods with no good results so now I'm deferring to those who have more experience with layout management than I. Thank you for your time.
You have to set the scrollArea to MyScrollWidget using a layout.
from PyQt5 import QtWidgets
import sys
class MyScrollWidget(QtWidgets.QWidget):
def __init__(self):
super(MyScrollWidget, self).__init__()
lay = QtWidgets.QVBoxLayout(self)
scrollArea = QtWidgets.QScrollArea()
lay.addWidget(scrollArea)
top_widget = QtWidgets.QWidget()
top_layout = QtWidgets.QVBoxLayout()
for i in range(10):
group_box = QtWidgets.QGroupBox()
group_box.setTitle('GroupBox For Item {0}'.format(i))
layout = QtWidgets.QHBoxLayout(group_box)
label = QtWidgets.QLabel()
label.setText('Label For Item {0}'.format(i))
layout.addWidget(label)
push_button = QtWidgets.QPushButton(group_box)
push_button.setText('Run Button')
push_button.setFixedSize(100, 32)
layout.addWidget(push_button)
top_layout.addWidget(group_box)
top_widget.setLayout(top_layout)
scrollArea.setWidget(top_widget)
self.resize(200, 500)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
widget = MyScrollWidget()
widget.show()
sys.exit(app.exec_())

Automatic resize QScrollArea

I have a scroll area containing a list of checkboxes. I would like this scroll area to resize to the size of the application. Can't figure out how to do this. Any suggestions would be appreciated. I have come this problem in other posts but have not found a solution, or at least a solution I understood.
from PyQt4 import QtGui, QtCore
class Screen3(QtGui.QWidget): # Screen to display data
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
# layout for check box container widget
chkBoxLayout = QtGui.QVBoxLayout(self)
# create list of checkboxes
self.checkboxes = []
numofCheckboxes = 30
for x in range(numofCheckboxes):
self.checkboxes.append(QtGui.QCheckBox('test'))
# add checkboxes to chkBoxLayout
for i, chkbox in enumerate(self.checkboxes):
chkbox.setChecked(True)
chkBoxLayout.addWidget(chkbox)
chkBoxLayout.addStretch(1)
chkBoxLayout.setMargin(0);
chkBoxLayout.setContentsMargins(0,0,0,0)
chkBoxLayout.setSpacing(0)
# checkbox container widget
widget = QtGui.QWidget()
widget.setStyleSheet(""".QWidget {background-color: rgb(255, 255, 255);}""")
widget.setLayout(chkBoxLayout)
# checkbox scroll area, gives scrollable view on widget
scroll = QtGui.QScrollArea()
scroll.setMinimumWidth(120)
scroll.setMinimumHeight(200) # would be better if resizable
scroll.setWidgetResizable(True)
scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
scroll.setWidget(widget)
self.samplePanel = QtGui.QVBoxLayout()
self.samplePanel.addWidget(scroll)
self.samplePanel.addStretch(1)
# horizontal layout
hbox = QtGui.QHBoxLayout()
hbox.addLayout(self.samplePanel)
hbox.addStretch(1)
self.setLayout(hbox)
self.resize(100, 500)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
Screen = Screen3()
Screen.show()
sys.exit(app.exec_())
You have to add the QScrollArea to the main layout, you do not need to create other layouts.
class Screen3(QtGui.QWidget): # Screen to display data
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
# layout for check box container widget
chkBoxLayout = QtGui.QVBoxLayout()
# create list of checkboxes
self.checkboxes = []
numofCheckboxes = 30
for x in range(numofCheckboxes):
self.checkboxes.append(QtGui.QCheckBox('test'))
# add checkboxes to chkBoxLayout
for i, chkbox in enumerate(self.checkboxes):
chkbox.setChecked(True)
chkBoxLayout.addWidget(chkbox)
chkBoxLayout.addStretch(1)
chkBoxLayout.setMargin(0);
chkBoxLayout.setContentsMargins(0,0,0,0)
chkBoxLayout.setSpacing(0)
# checkbox container widget
widget = QtGui.QWidget()
widget.setStyleSheet(""".QWidget {background-color: rgb(255, 255, 255);}""")
widget.setLayout(chkBoxLayout)
# checkbox scroll area, gives scrollable view on widget
scroll = QtGui.QScrollArea()
scroll.setMinimumWidth(120)
scroll.setMinimumHeight(200) # would be better if resizable
scroll.setWidgetResizable(True)
scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
scroll.setWidget(widget)
lay = QtGui.QVBoxLayout(self)
lay.addWidget(scroll)

How can I make a PyQt widget resizable by dragging?

I have a QScrollArea containing a widget with a QVBoxLayout. Inside this layout are several other widgets. I want the user to be able to drag the lower borders of those widgets to resize them in the vertical direction. When they are resized, I don't want them to "steal" size from the other widgets in the scrolling area; instead I want the entire scrolled "page" to change its size. So if you enlarge one of the widgets, it should push the other widgets down (out of the viewport of the scroll area); if you shrink it, it should pull the other widgets up. Dragging the border of one widget should not change the size of any of the other widgets in the vertical scroll; it should just move them.
I began by using a QSplitter. If I use that, I can drag to change the size of a widget, but there doesn't seem to be a way to get it to "push/pull" the others as I described above, rather than growing/shrinking them. But I can't find any other way to give a widget a draggable handle that will allow me to change its size. How can I accomplish this?
Here is a simple example of what I'm doing. (In this example I've commented out the splitter, but if you uncomment it you can see what happens with that version.)
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.Qsci import QsciScintilla, QsciLexerPython
class SimplePythonEditor(QsciScintilla):
def __init__(self, parent=None):
super(SimplePythonEditor, self).__init__(parent)
self.setMinimumHeight(50)
class Chunk(QFrame):
def __init__(self, parent=None):
super(Chunk, self).__init__(parent)
layout = QVBoxLayout(self)
sash = QSplitter(self)
layout.addWidget(sash)
sash.setOrientation(Qt.Vertical)
editor = self.editor = SimplePythonEditor()
output = self.output = SimplePythonEditor()
output.setReadOnly(True)
sash.addWidget(editor)
sash.addWidget(output)
self.setLayout(layout)
print(self.sizePolicy())
class Widget(QWidget):
def __init__(self, parent= None):
global inout
super(Widget, self).__init__()
#Container Widget
widget = QWidget()
#Layout of Container Widget
layout = QVBoxLayout(self)
#sash = QSplitter(self)
#layout.addWidget(sash)
#sash.setOrientation(Qt.Vertical)
for num in range(5):
editor = SimplePythonEditor()
editor.setText("Some stuff {}".format(num))
layout.addWidget(editor)
#sash.addWidget(editor)
widget.setLayout(layout)
#Scroll Area Properties
scroll = QScrollArea()
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scroll.setWidgetResizable(True)
scroll.setWidget(widget)
scroll.setMaximumHeight(500)
#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_()
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.setWindowTitle("MainWindow")
MainWindow.resize(500, 500)
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
MainWindow.setCentralWidget(self.centralwidget)
QMetaObject.connectSlotsByName(MainWindow)
class Ewindow(QMainWindow,QApplication):
"""docstring for App"""
resized = pyqtSignal()
def __init__(self,parent):
super(Ewindow,self).__init__(parent=parent)
self.setGeometry(500, 500, 800,800)
self.setWindowTitle('Mocker')
self.setWindowIcon(QIcon('icon.png'))
self.setAttribute(Qt.WA_DeleteOnClose)
ui2 = Ui_MainWindow()
ui2.setupUi(self)
self.resized.connect(self.readjust)
def resizeEvent(self, event):
self.resized.emit()
return super(Ewindow, self).resizeEvent(event)
def readjust(self):
self.examForm.move(self.width()-self.examForm.width(),0)
self.btn_skip.move(self.width()-self.btn_skip.width(),self.height()-100)
self.btn_next.move(self.btn_showAnswers.x()+self.btn_showAnswers.width(),self.height()-100)
self.btn_prev.move(0,self.height()-100)
self.btn_showAnswers.move(self.btn_prev.x()+self.btn_prev.width(),self.height()-100)
self.btn_home.move(self.width()-200,self.height()-150)
self.lbscreen1.resize(self.width()-self.examForm.width(),self.height()-200)
self.examForm.resize(200,self.height()-150)
self.btn_skip.resize(self.examForm.width(),100)
self.btn_next.resize(self.btn_prev.width(),100)
self.btn_prev.resize(self.width()*0.25,100)
self.btn_showAnswers.resize(self.btn_prev.width(),100)
self.btn_home.resize(200,50)
here is an example code of a resizable window it moves and stretches widgets as you resize the window. The idea is to keep widget coordinates and sizes relative to each other.
so i had to make a class Ui_MainWindow and set it for my window class ui2.setupUi(self) and also declare the resized = pyqtSignal() which i'd be using to run the readjust function which resets size and coordinates of the widgets like so self.resized.connect(self.readjust).
i hope this helps!

Categories

Resources