I have two buttons (wgtbtnA & wgtbtnB) placed on two different pages (page1 and page2, respectively) inside a parent object (objectNa
If you really need arrows in your stacked widget, another solution is to implement your own "Promoted Widget": it allows you to create your design with custom widgets, that might extend the basic widgets provided by Qt. You won't be able to interact with your own implementation in Designer, but you'll get the result once you run your program.
This is the procedure: create your own subclass of the widget you want to extend, define your custom methods or override existing ones (remember that some private methods require a specific return value type, check the documentation). It's usually better to save the subclass(es) you created in a separate files. Then, in Designer add the widget you need (in this case, StackedWidget), right click on it and select "Promote to..."; in the dialog that will be shown, type the subclass name you created in the "Promoted class name" field (in the example below, it will be "StackedWidgetWithArrowButtons") and the file that contains it in the "header file" field: it will be treated as a python import, so do not add the trailing ".py" and remember that if you saved it in a subdirectory you'll need the full "module" path, for example "mysubclasses.customstackwidget", if the file is "customstackwidget" in the "mysubclasses" directory. Save the ui, compile it and run the program.
class StackedWidgetWithArrowButtons(QtWidgets.QStackedWidget):
def __init__(self, *args, **kwargs):
QtWidgets.QStackedWidget.__init__(self, *args, **kwargs)
self.backwardButton = QtWidgets.QToolButton(self)
self.backwardButton.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_ArrowLeft))
self.backwardButton.setMaximumSize(24, 24)
self.backwardButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.forwardButton = QtWidgets.QToolButton(self)
self.forwardButton.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_ArrowRight))
self.forwardButton.setMaximumSize(24, 24)
self.forwardButton.setFocusPolicy(QtCore.Qt.NoFocus)
self.currentChanged.connect(self.checkSwitchButtons)
def checkSwitchButtons(self):
self.forwardButton.setEnabled(self.currentIndex() < self.count() - 1)
self.backwardButton.setEnabled(self.currentIndex() > 0)
def addWidget(self, widget):
# this is a private method of QStackedWidget that is called when
# the ui is being built by the program, we just implement it
# to ensure that the buttons are correctly enabled;
# the index *has* to be returned
index = QtWidgets.QStackedWidget.addWidget(self, widget)
self.checkSwitchButtons()
return index
def removeWidget(self, widget):
# not necessary, but in case you want to remove widgets in the
# future, it will check buttons again
index = QtWidgets.QStackedWidget.removeWidget(self, widget)
self.checkSwitchButtons()
return index
def mousePressEvent(self, event):
# due to the way QStackedWidget is implemented, children widgets
# that are not in its layout might not receive mouse events,
# but we just need to track clicks so this is enough
if event.button() == QtCore.Qt.LeftButton:
if event.pos() in self.backwardButton.geometry():
self.setCurrentIndex(self.currentIndex() - 1)
elif event.pos() in self.forwardButton.geometry():
self.setCurrentIndex(self.currentIndex() + 1)
def resizeEvent(self, event):
# the base class resizeEvent *has* to be called, otherwise
# you could encounter problems with children widgets
QtWidgets.QStackedWidget.resizeEvent(self, event)
# now ensure that the buttons are always placed on the top
# right corner; this positioning is completely manual and you
# have to take button sizes in consideration to avoid
# overlapping buttons; obviously you can place them wherever
# you want.
self.forwardButton.move(self.rect().right() - self.forwardButton.width(), 0)
self.backwardButton.move(self.forwardButton.x() - self.backwardButton.width(), 0)
If you don't want buttons (or you don't like the way they appear) you could implement your own paintEvent. In this case I created small triangles using QPolygons.
class StackedWidgetWithTriangles(QtWidgets.QStackedWidget):
def __init__(self, *args, **kwargs):
QtWidgets.QStackedWidget.__init__(self, *args, **kwargs)
self.backwardRect = QtCore.QRect(0, 0, 16, 16)
self.forwardRect = QtCore.QRect(0, 0, 16, 16)
self.forwardArrow = QtGui.QPolygon([QtCore.QPoint(-6, -6), QtCore.QPoint(6, 0), QtCore.QPoint(-6, 6)])
self.backwardArrow = QtGui.QPolygon([QtCore.QPoint(6, -6), QtCore.QPoint(-6, 0), QtCore.QPoint(6, 6)])
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
if event.pos() in self.backwardRect:
self.setCurrentIndex(self.currentIndex() - 1)
elif event.pos() in self.forwardRect:
self.setCurrentIndex(self.currentIndex() + 1)
def resizeEvent(self, event):
QtWidgets.QStackedWidget.resizeEvent(self, event)
self.forwardRect.moveLeft(self.rect().right() - self.forwardRect.width())
self.backwardRect.moveLeft(self.forwardRect.x() - self.forwardRect.width())
def paintEvent(self, event):
qp = QtGui.QPainter(self)
qp.setRenderHints(qp.Antialiasing)
# set colors according to the possibility of going back or forward,
# showing a "disabled" arrow whenever it's not possible
if self.currentIndex() > 0:
qp.setPen(QtCore.Qt.darkGray)
qp.setBrush(QtCore.Qt.black)
else:
qp.setPen(QtCore.Qt.lightGray)
qp.setBrush(QtCore.Qt.transparent)
qp.drawPolygon(self.backwardArrow.translated(self.backwardRect.center()))
if self.currentIndex() < self.count() - 1:
qp.setPen(QtCore.Qt.darkGray)
qp.setBrush(QtCore.Qt.black)
else:
qp.setPen(QtCore.Qt.lightGray)
qp.setBrush(QtCore.Qt.transparent)
qp.drawPolygon(self.forwardArrow.translated(self.forwardRect.center()))