In reference to this answer on adding a new tab button to QTabWidget,
I am unsure where the QPushButton is added to the QTabBar.
I assume the setParent method on the pushButton adds it to the tab bar.
But when I try to implement it, the pushButton doesnt seem to appear anywhere on the tab bar even if I add hard values to the move operation.
Here is a minimum reproducible example,
from PyQt5 import QtGui, QtCore, QtWidgets
class TabBarPlus(QtWidgets.QTabBar):
"""Tab bar that has a plus button floating to the right of the tabs."""
plusClicked = QtCore.pyqtSignal()
def __init__(self):
super().__init__()
# Plus Button
self.plusButton = QtWidgets.QPushButton("+")
self.plusButton.setParent(self)
self.plusButton.setFixedSize(20, 20) # Small Fixed size
self.plusButton.clicked.connect(self.plusClicked.emit)
self.movePlusButton() # Move to the correct location
# end Constructor
def sizeHint(self):
"""Return the size of the TabBar with increased width for the plus button."""
sizeHint = QtWidgets.QTabBar.sizeHint(self)
width = sizeHint.width()
height = sizeHint.height()
return QtCore.QSize(width+25, height)
# end tabSizeHint
def resizeEvent(self, event):
"""Resize the widget and make sure the plus button is in the correct location."""
super().resizeEvent(event)
self.movePlusButton()
# end resizeEvent
def tabLayoutChange(self):
"""This virtual handler is called whenever the tab layout changes.
If anything changes make sure the plus button is in the correct location.
"""
super().tabLayoutChange()
self.movePlusButton()
# end tabLayoutChange
def movePlusButton(self):
"""Move the plus button to the correct location."""
# Find the width of all of the tabs
size = sum([self.tabRect(i).width() for i in range(self.count())])
# size = 0
# for i in range(self.count()):
# size += self.tabRect(i).width()
# Set the plus button location in a visible area
h = self.geometry().top()
w = self.width()
if size > w: # Show just to the left of the scroll buttons
self.plusButton.move(w-54, h)
else:
self.plusButton.move(size, h)
# end movePlusButton
# end class MyClass
class CustomTabWidget(QtWidgets.QTabWidget):
"""Tab Widget that that can have new tabs easily added to it."""
def __init__(self, parent=None):
super().__init__(parent)
# Tab Bar
self.tab = TabBarPlus()
self.setTabBar(self.tab)
# Properties
self.setMovable(True)
self.setTabsClosable(True)
# Signals
self.tab.plusClicked.connect(self.addTab)
# self.tab.tabMoved.connect(self.moveTab)
# self.tabCloseRequested.connect(self.removeTab)
# end Constructor
# end class CustomTabWidget
class AppDemo(QtWidgets.QMainWindow):
def __init__(self):
super(AppDemo, self).__init__()
self.centralwidget = QtWidgets.QWidget(self)
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setContentsMargins(0, -1, 0, -1)
self.playlist_manager = CustomTabWidget(self.centralwidget)
self.horizontalLayout.addWidget(self.playlist_manager)
blankWidget = QtWidgets.QWidget(self.playlist_manager)
self.playlist_manager.addTab(blankWidget, "New")
self.setCentralWidget(self.centralwidget)
self.show()
# end class AppDemo
def main():
import sys
app = QtWidgets.QApplication(sys.argv)
w = AppDemo()
w.setWindowTitle('AppDemo')
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Expected behvaiour is that a "+" button appears at the right of all the tabs,
but no such button appears.
Okay so after some brainstorming, I figured the issue out. Unlike PyQt4. The QTabBar width does not span the entire width of the QTabWidget, and as such the PlusButton when moved to the right of all the tabs will exceed the width of the parent widget and disappear.
The Solution to this is to add the QPushButton in the QTabWidget itself and emit layoutchange from the QTabBar.
Here is a working example, I have modified values to fit my use case
class tabBarPlus(QTabBar):
layoutChanged = pyqtSignal()
def resizeEvent(self, event):
super().resizeEvent(event)
self.layoutChanged.emit()
def tabLayoutChange(self):
super().tabLayoutChange()
self.layoutChanged.emit()
class customTabWidget(QTabWidget):
plusClicked = pyqtSignal()
def __init__(self, parent=None):
super(customTabWidget, self).__init__(parent)
self.tab = tabBarPlus()
self.setTabBar(self.tab)
self.plusButton = QPushButton('+', self)
self.plusButton.setFixedSize(35, 25)
self.plusButton.clicked.connect(self.plusClicked.emit)
self.setMovable(True)
self.setTabsClosable(True)
self.tab.layoutChanged.connect(self.movePlusButton)
def movePlusButton(self):
size = sum([self.tab.tabRect(i).width() for i in range(self.tab.count())])
h = max(self.tab.geometry().bottom() - 24, 0)
w = self.tab.width()
print(size, w, h)
if size > w:
self.plusButton.move(w-self.plusButton.width(), h)
else:
self.plusButton.move(size-2, h)
Related
I am designing an app to draw electrical circuit models and I need to insert elements on scene by clicking on a button and after on the scene.
The way it should work is the following: I press the button to insert a node, then it shows a message saying "Press where you want to insert the element", you press, and the element appear on screen.
I think the problem is that I have to stop the code to get the position and then continue or something like that.
Below I show a part of the code (the original contains more classes and a second tab for calculations, that it is not needed for this trouble and it is not connected in any way):
from PyQt5 import sip
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import QRectF, Qt, QSize, QPointF, QPoint, QLineF, showbase
from PIL import Image
import calculation
class Main(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Nodal Equations")
self.setWindowIcon(QIcon("images/flash.ico"))
self.setGeometry(150,50,1650,950)
self.setFixedSize(self.size())
self.UI()
self.show()
def UI(self):
self.toolBar()
self.tabwidgets()
self.widgets()
self.layouts()
def toolBar(self):
self.tb = QToolBar("Toolbar")
self.addToolBar(Qt.RightToolBarArea, self.tb)
self.tb.setIconSize(QSize(50,50))
# Toolbar buttons
self.addNode = QAction(QIcon("images/node.png"),"Node",self)
self.tb.addAction(self.addNode)
self.addNode.triggered.connect(self.funcAddNode)
self.tb.addSeparator()
def tabwidgets(self):
self.tabs = QTabWidget()
self.tabs.blockSignals(True) # To update tabs. After defining every layout we have to set it to False
self.setCentralWidget(self.tabs) # If this is not used, we cannot see the widgets (Needed if we use QMainWindow)
self.modelViewTab = QWidget()
self.tabs.addTab(self.modelViewTab, "Model View")
def widgets(self):
# Model View widgets
self.view = ModelView()
self.instructionsLabel = QLabel("Welcome to Node Equations Solver. To start select an order on the toolbar menu", self)
def layouts(self):
# Model View Tab layouts
self.mainModelLayout = QVBoxLayout()
self.sceneLayout = QHBoxLayout()
self.instructionsLayout = QHBoxLayout()
self.mainModelLayout.setAlignment(Qt.AlignCenter)
##### Adding widgets
self.sceneLayout.addWidget(self.view)
self.instructionsLayout.addWidget(self.instructionsLabel)
##### Adding layouts
self.mainModelLayout.addLayout(self.sceneLayout)
self.mainModelLayout.addLayout(self.instructionsLayout)
self.modelViewTab.setLayout(self.mainModelLayout)
def funcAddNode(self):
self.node = Node(0,500,500)
self.view.scene.addItem(self.node)
class Node(QGraphicsEllipseItem):
def __init__(self,number, x, y):
super(Node, self).__init__(0, 0, 10, 10)
self.number = number
self.setPos(x, y)
self.setBrush(Qt.yellow)
self.setAcceptHoverEvents(True)
# Mouse hover events
def hoverEnterEvent(self, event):
app.instance().setOverrideCursor(Qt.OpenHandCursor)
def hoverLeaveEvent(self, event):
app.instance().restoreOverrideCursor()
# Mouse click events
def mousePressEvent(self, event):
app.instance().setOverrideCursor(Qt.ClosedHandCursor)
def mouseReleaseEvent(self, event):
app.instance().restoreOverrideCursor()
def mouseMoveEvent(self, event):
orig_cursor_position = event.lastScenePos()
updated_cursor_position = event.scenePos()
orig_position = self.scenePos()
updated_cursor_x = updated_cursor_position.x() - orig_cursor_position.x() + orig_position.x()
updated_cursor_y = updated_cursor_position.y() - orig_cursor_position.y() + orig_position.y()
if updated_cursor_x < 0:
self.setPos(QPointF(0, updated_cursor_y))
elif updated_cursor_y < 0:
self.setPos(QPointF(updated_cursor_x, 0))
elif updated_cursor_x + self.boundingRect().right() > 1550:
self.setPos(QPointF(1550 - self.boundingRect().width(), updated_cursor_y))
elif updated_cursor_y + self.boundingRect().bottom() > 840:
self.setPos(QPointF(updated_cursor_x, 840 - self.boundingRect().height()))
else:
self.setPos(QPointF(updated_cursor_x, updated_cursor_y))
class ModelView(QGraphicsView):
def __init__(self):
super().__init__()
self.setRenderHints(QPainter.Antialiasing)
self.scene = QGraphicsScene()
self.setScene(self.scene)
self.setSceneRect(0, 0, 1550, 840)
##### This is a way I tried to pick the cursor position and store it, but it didn't work
# def mousePressEvent(self, event):
# x = event.scenePos().x()
# y = event.scenePos().y()
# return (x,y)
def main():
global app
app = QApplication(sys.argv)
window = Main()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
You need to change the return (x,y) to something useful, i.e. emitting a signal or actually adding an element.
mousePressEvent is a method that does not return anything (void in C++).
I'm trying to create a compound widget similiar to the following:
A Rectangle overlayed with a button that is partially outside the rectangle's bounds.
Here is the code corresponding to that image:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QPoint
from PyQt5.QtGui import QResizeEvent
class MyWidget(QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.layout = QHBoxLayout()
self.layout.setContentsMargins(0, 0, 0, 0)
self.setLayout(self.layout)
self.lbl = QLabel()
self.lbl.setStyleSheet('background: #EE6622')
self.lbl.setFixedSize(125, 150)
self.layout.addWidget(self.lbl)
self.btn = QPushButton(parent=self)
self.btn.setStyleSheet('background: #ABCDEF')
self.btn.setFixedSize(25, 25)
def resizeEvent(self, event: QResizeEvent) -> None:
super().resizeEvent(event)
self.update_btn_pos()
def update_btn_pos(self):
pos = (
self.lbl.pos() +
QPoint(
self.lbl.rect().width() - int(self.btn.width() / 2),
-int(self.btn.height() / 2))
)
self.btn.move(pos)
if __name__ == "__main__":
a = QApplication(sys.argv)
window = MyWidget()
window.show()
a.exec()
My problem is that the widget's behaviour when resizing suggests that the button is not really "part of" that widget - it is cut-off as if it weren't there:
I tried to overwrite the sizeHint()-method to include the button, but that only solves the problem on startup, I can still resize the window manually to cut the button off again.
What must be changed in order to make this work?
I think I might have found a solution myself by adding the following to the __init__ - method:
self.layout.setContentsMargins(
0,
int(self.btn.height() / 2),
int(self.btn.width() / 2),
0
)
By setting the contentsMargin, the size of the big rectangle doesn't change because it is fixed and the parent widget still covers the space under the button:
I'm not sure if this is the *right way* to do it though ...
Alright, thanks to #musicamante this is the final code:
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import QResizeEvent
class MyWidget(QWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.layout = QHBoxLayout()
self.setLayout(self.layout)
self.lbl = QLabel()
self.lbl.setStyleSheet('background: #EE6622')
self.lbl.setFixedSize(125, 150)
self.layout.addWidget(self.lbl)
self.btn = QPushButton(parent=self)
self.btn.setStyleSheet('background: #ABCDEF')
self.btn.setFixedSize(25, 25)
# set contents margin of layout to half the button's size
self.layout.setContentsMargins(
*([int(self.btn.height() / 2), int(self.btn.width() / 2)]*2)
)
def resizeEvent(self, event: QResizeEvent) -> None:
super().resizeEvent(event)
self.update_btn_pos()
def update_btn_pos(self):
rect = self.btn.rect()
rect.moveCenter(self.lbl.geometry().topRight())
self.btn.move(rect.topLeft())
if __name__ == "__main__":
a = QApplication(sys.argv)
window = MyWidget()
window.show()
a.exec()
Result:
On paint event widget paints itself and all of his children clipped to his bounds. You can try to set button parent to MyWidget's parent, but you'll still have problem of button blocking part of some other widget or clipping on window's client area.
On the other hand there is no much difference between hovering button thats inside parent's widget and hovering button that sticks out, messing with other widgets.
In the following code, I'm hiding non-selected items in a QListWidget. (self.field is an
instance of QListWidget).
# Make selected items visible and other items hidden:
for i in range(self.field.count()):
self.field.item(i).setHidden(not self.field.item(i).isSelected())
When I hide non-selected items, they are essentially whited out, i.e., the space does not close up (see below). Is there any way to close up the whitespace?
Here is an example of a list widget with a dynamic height. This is achieved by subclassing QListWidget and overriding viewportSizeHint and minimumSizeHint. The size adjust policy is set to AdjustToContents so that the list widget will update its size when its contents have changed. The horizontal scroll bar is set to ScrollBarAlwaysOff to avoid the extra vertical space added at the bottom of the list widget that is reserved for the horizontal scroll bar if it were to be shown.
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import Qt
class ListWidget(QtWidgets.QListWidget):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
def minimumSizeHint(self) -> QtCore.QSize:
return QtCore.QSize(-1, -1)
def viewportSizeHint(self) -> QtCore.QSize:
if self.model().rowCount() == 0:
return QtCore.QSize(self.width(), 0)
height = sum(self.sizeHintForRow(i) for i in range(self.count()) if not self.item(i).isHidden())
width = super().viewportSizeHint().width()
return QtCore.QSize(width, height)
class Widget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
# create and populate ListWidget instance
self.list_widget = ListWidget()
self.list_widget.setSelectionMode(QtWidgets.QListWidget.MultiSelection)
self.list_widget.addItems('Aardvark Bear Cat Donkey Echidna Ferret Goose'.split())
# setup rest of gui
self.select_button = QtWidgets.QPushButton('Select')
self.clear_button = QtWidgets.QPushButton('Clear selection')
vlayout = QtWidgets.QVBoxLayout(self)
vlayout.addWidget(QtWidgets.QLabel('Choose your mascots'))
vlayout.addWidget(self.list_widget)
# stretch added to layout to accommodate for varying height of list widget
vlayout.addStretch(1)
hlayout = QtWidgets.QHBoxLayout()
hlayout.addWidget(self.select_button)
hlayout.addWidget(self.clear_button)
self.select_button.setDefault(True)
vlayout.addLayout(hlayout)
self.select_button.clicked.connect(self.select)
self.clear_button.clicked.connect(self.clear_selection)
def select(self):
for i in range(self.list_widget.count()):
self.list_widget.item(i).setHidden(not self.list_widget.item(i).isSelected())
def clear_selection(self):
self.list_widget.clearSelection()
for i in range(self.list_widget.count()):
self.list_widget.item(i).setHidden(False)
if __name__ == '__main__':
app = QtWidgets.QApplication([])
win = Widget()
win.show()
app.exec()
Screenshot:
I'm using QFrame to make 'cards' with drop shadows using QGraphicsDropShadowEffect. The issue I'm having, is if I add a button to the card, then add a drop shadow to the button, the button is invisible but still clickable. If I remove the drop shadow from the card, it shows the button fine, or if I remove the shadow from the button, it's visible. It seems I can't have drop shadows on both the card and the button.
My classes:
class Card(QFrame):
""" """
def __init__(self, title='Card Title', cls_layout=QVBoxLayout,
vsizing=QSizePolicy.Preferred, hsizing=QSizePolicy.Preferred,
has_shadow=False, subtitle='', parent=None):
super().__init__(parent)
self.has_shadow = has_shadow
self.setStyleSheet('QFrame { border-radius: 2px; background: white;}')
self._layout = QVBoxLayout(self)
self._layout.setAlignment(Qt.AlignTop)
self._lblTitle = QLabel(title)
self._lblTitle.setFont(make_font(pt=12, b=True))
self._lblSubtitle = QLabel()
self._lblSubtitle.setIndent(4)
self._layout.addWidget(self._lblTitle)
self._layout.addWidget(self._lblSubtitle)
self._layout.addSpacing(4)
#self.subtitle = subtitle # property not used for example
self.enabled = True
self.layout = cls_layout() # where content is added
self._layout.addLayout(self.layout)
self.setSizePolicy(hsizing, vsizing)
#property
def enabled(self):
return self.isEnabled()
#enabled.setter
def enabled(self, state):
if state and self.has_shadow:
effect = QGraphicsDropShadowEffect(self)
effect.setOffset(1, 2)
effect.setBlurRadius(4)
self.setGraphicsEffect(effect)
else:
self.setGraphicsEffect(None)
self.setEnabled(state)
class PushButton(QPushButton):
def __init__(self, text, width=75, height=30, parent=None):
super().__init__(text, parent)
self.setFixedWidth(width)
self.setFixedHeight(height)
self.enabled = True
#property
def enabled(self):
return self.isEnabled()
#enabled.setter
def enabled(self, state):
self.setGraphicsEffect(None)
if state:
self.effect = QGraphicsDropShadowEffect(self)
self.effect.setOffset(1, 2)
self.effect.setBlurRadius(4)
self.setGraphicsEffect(self.effect)
self.setEnabled(state)
This is an example of how I'm using them:
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout
import sys
if __name__ == '__main__':
app = QApplication([])
window = QMainWindow()
widget = QWidget()
layout = QVBoxLayout(widget)
window.setCentralWidget(widget)
card = Card(has_shadow=True)
btn = PushButton('Test Button')
btn.clicked.connect(print)
card.layout.addWidget(btn)
btn.enabled = False # Button Visible
# btn.enabled = True # Button Invisible, but still click-able
layout.addWidget(card)
window.show()
sys.exit(app.exec_())
I tried ditching the drop shadow on the card and setting the QFrame to StyledPanel with Raised shadow, but I can't get them to show up, even if I remove the css from it. I'm using Windows 7 with Anaconda 4.4 if that makes a difference. I'm assuming I'm either using the graphics effect wrong or doing something else incorrectly, but I haven't been able to find any other posts with a similar issue.
I ended up using this method and changing my button class to:
class Button(QWidget):
clicked = pyqtSignal()
def __init__(self, text, w=75, h=50, parent=None):
super().__init__(parent)
layout = QVBoxLayout(self)
layout.setContentsMargins(QMargins(0, 0, 0, 0))
self.setFixedSize(w + 5, h + 6)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.scene = QtWidgets.QGraphicsScene(self)
self.view = QtWidgets.QGraphicsView(self)
self.view.setScene(self.scene)
self._btn = QPushButton(text)
self._btn.setFixedSize(w, h)
self._btn.setAttribute(Qt.WA_TranslucentBackground)
self._btn.setStyleSheet(gui.css.button_css())
self._btn.clicked.connect(self.click)
self.btn = self.scene.addWidget(self._btn)
self.enabled = True
layout.addWidget(self.view)
#property
def enabled(self):
return self._btn.isEnabled()
#enabled.setter
def enabled(self, state):
self._btn.setEnabled(state)
if state:
shadow = QGraphicsDropShadowEffect(self)
shadow.setOffset(1, 2)
shadow.setBlurRadius(4)
self.btn.setGraphicsEffect(shadow)
else:
self.btn.setGraphicsEffect(None)
def click(self):
self.clicked.emit()
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!