Issue
I'm implementing a UI, which it contains several buttons on top, and there's a QStackedLayout object under those buttons, the purpose of clicking on different buttons should automatically switch to its associated QWidget, which is inside the QStackedLayout object. And the amount of buttons and their associated QWidget is dynamically added.
For example, here I want to implement a simple window that holds two buttons 'a' and 'b', ideally when clicking on the'a' button, you should see the label 'a' below, which means the QStackedLayout is set to an appropriate widget that holds the QLabel 'a', similar rules should apply for button 'b' as well. so I wrote the following codes:
import sys
from PySide.QtCore import *
from PySide.QtGui import *
class main_gui(QWidget):
def __init__(self, parent = None):
super(main_gui, self).__init__(parent)
#create a root level widget
self.main_widget = QWidget()
#create a root level layout that holds all the child widgets
self.main_layout = QVBoxLayout(self.main_widget)
# widgets for buttons layout
self.asset_type_buttons_widget = QWidget()
# layout for all buttons
self.asset_type_buttons_layout = QHBoxLayout(self.asset_type_buttons_widget)
# now create the stack layout
self.asset_stacked_widget = QWidget()
self.asset_stacked_layout = QStackedLayout(self.asset_stacked_widget)
# add the buttons and stack layout into the root level layout
self.main_layout.addWidget(self.asset_type_buttons_widget)
self.main_layout.addWidget(self.asset_stacked_widget)
#dynamically adds buttons and stacked widgets, here we're going to add 2 buttons, which their labels are 'a' and 'b'
self.create_asset_sections(self.asset_stacked_layout, self.asset_type_buttons_layout, 'a','b')
self.main_widget.show()
def create_asset_sections(self, stack_layout, parent_buttons_layout, *asset_types):
# asset_types represents any arbitrary number of buttons and their associated stacked widgets
for type in asset_types:
index = asset_types.index(type)
self.type_top_button = QPushButton(type)
parent_buttons_layout.addWidget(self.type_top_button)
self.type_top_widget = QWidget()
self.type_top_layout = QVBoxLayout(self.type_top_widget)
i = stack_layout.insertWidget(index, self.type_top_widget)
self.test_label = QLabel(type)
self.type_top_layout.addWidget(self.test_label)
self.type_top_button.clicked.connect(lambda: stack_layout.setCurrentIndex(i))
app = QApplication(sys.argv)
gui = main_gui()
app.exec_()
But executed above codes you should notice that label 'a' only appears once, then no matter you click on button 'a' or button 'b', label 'b' is always being shown under the buttons.
I also tried to modified the codes then used QStackedLayout.setCurrentWidget() method, and here's another version of create_asset_sections()
def create_asset_sections(self, stack_layout, parent_buttons_layout, *asset_types):
# create a dictionary that holds the buttons and their associated stack widgets
self.map_buttons_widgets = {}
for type in asset_types:
self.type_top_button = QPushButton(type)
parent_buttons_layout.addWidget(self.type_top_button)
self.type_top_widget = QWidget()
self.type_top_layout = QVBoxLayout(self.type_top_widget)
stack_layout.addWidget(self.type_top_widget)
self.test_label = QLabel(type)
self.type_top_layout.addWidget(self.test_label)
self.map_buttons_widgets[self.type_top_button] = self.type_top_widget
for btn, wigt in self.map_buttons_widgets.iteritems():
btn.clicked.connect(lambda: stack_layout.setCurrentWidget(wigt))
So use above methods to replace the previous one doesn't help achieve my original purpose either.
Any thoughts? Thank you for your answers in advance.
Solution
See this question. It's a different question, but they're having essentially the same problem. This line is the issue
self.type_top_button.clicked.connect(lambda: stack_layout.setCurrentIndex(i))
Specifically
lambda: stack_layout.setCurrentIndex(i)
The problem is that the value of i
is not evaluated when you create the lambda function. It's evaluated when the lambda function is called, and by that point, the loop has finished and every lambda
will be using the exact same value of i
at the end of the loop, not the value of i
when the lambda
was created.
You can fix this two ways. The first is to use default arguments in your lambda
, which forces the value of i
to be evaluated at function creation time.
lambda i=i: stack_layout.setCurrentIndex(i)
Alternatively, you can use functools.partial
from functools import partial
self.type_top_button.clicked.connect(partial(layout.setCurrentIndex, i))
Answered By - Brendan Abel
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.