DEV Community

Shamith Nakka
Shamith Nakka

Posted on • Edited on

Facing issues while creating multiple instances of Dependent QComboBox

I'm working on the GUI part of the input dock on the truss_connection_bolted module on a project called Osdag and the whole GUI part is written using the PyQt5 module. My task is to finish the connecting members attribute on the Input dock which happens to be a set of QComboBox widgets on a single QTableWidget. I have successfully written most of the part by there is one section of QComboBox in the Table widget that depends on another QComboBox.

After long research, I was able to create a stud that shows the required functionality. Here is the following code snippet for the stub.

# --------------- # More Code above # ---------------  # Table Widget Creation table_widget = QtWidgets.QTableWidget(self.dockWidgetContents) table_widget.setObjectName(option[0]) table_widget.setRowCount(8) table_widget.setColumnCount(6) # Attributes dictionary selectable_options = { 'Section profile': ['Angle', 'Star Angles', 'Back to Back Angles', 'Channel','Back to Back Channels'], 'Connection Location': { 'Angle': ['Long leg connected', 'Short leg connected'], 'Channel': ['Web-connected'] }, 'Section Size': ['20 x 20 x 3', '20 x 20 x 4', '25 x 25 x 3', '25 x 25 x 4', ...], 'Material': [ 'E165', 'E250 (Fe 410W) A', 'E250 (Fe 410W) B', 'E250 (Fe 410W) C', ....] } # Setting Head Labels to the table widget table_widget.setHorizontalHeaderLabels( list(selectable_options.keys()) + ["Angle with x-axis (Degrees)", "Factored Load (KN)"] ) # Creating two indivdual QComboBox for testing out the dependency functionality combo_box_secpro = QtWidgets.QComboBox() combo_box_connloc = QtWidgets.QComboBox() # Adding values to the "Section Profile" QComboBox for item in selectable_options['Section profile']: value = None if item in ['Angle', 'Star Angles', 'Back to Back Angles']: value = selectable_options['Connection Location']['Angle'] elif item in ['Channel', 'Back to Back Channels']: value = selectable_options['Connection Location']['Channel'] combo_box_secpro.addItem(item, value) # Connecting "Section Profile" QComboBox to "Connection Location" QComboBox combo_box_secpro.activated.connect( lambda: combo_box_connloc.clear() or combo_box_connloc.addItems( selectable_options['Connection Location'][ 'Angle' if combo_box_secpro.currentText() in ['Angle', 'Star Angles', 'Back to Back Angles'] else 'Channel'] ) ) # Placing both of those the QComboBoxes into table Widget table_widget.setCellWidget(0,0,combo_box_secpro) table_widget.setCellWidget(0,1,combo_box_connloc) # --------------- # More code below # --------------- 
Enter fullscreen mode Exit fullscreen mode

Output of stub code snippet

The above code snippet worked fine but, when I tried to loop the same logic... It's not working as Intended... The code snippet for the looping logic:

# --------------------- # More other code above # ---------------------  # Note that this is just a looped version of the above logic, # so all the other declarations of the dictionary mentioned above are present here..  combo_box_dictionary = dict() # Just in case, if we need the combo_box object for each option  for col, (key, value) in enumerate(selectable_options.items()): # Skipping 'Connection location' as I have handled it in 'Section profile'  if key == 'Connection Location': continue # Creating 'Connection location' QComboBox that depend on 'Section profile' QComboBox  elif key == 'Section profile': combo_box_section_profile_list = [] combo_box_connection_location_list = [] # Looping for 8 times for each row in table widget  for row in range(8): # Creating QComboBox Object for both 'Section profile' & 'Connection location'  combo_box_section_profile = QtWidgets.QComboBox() combo_box_connection_location = QtWidgets.QComboBox() # Adding items into 'Section profile' QComboBox  for item in selectable_options['Section profile']: value = None if item in ['Angle', 'Star Angles', 'Back to Back Angles']: value = selectable_options['Connection Location']['Angle'] elif item in ['Channel', 'Back to Back Channels']: value = selectable_options['Connection Location']['Channel'] combo_box_section_profile.addItem(item, value) # Connecting 'Section profile' dependent functionality to 'Connection Location'  combo_box_section_profile.activated.connect( lambda: combo_box_connection_location.clear() or combo_box_connection_location.addItems( selectable_options['Connection Location']['Angle' if combo_box_section_profile.currentText() in ['Angle', 'Star Angles', 'Back to Back Angles'] else 'Channel'] ) ) # Added Default Items into 'Connection Location'  combo_box_connection_location.addItems(selectable_options['Connection Location']['Angle']) # Setting the QComboBoxes into their respective places  table_widget.setCellWidget(row, col, combo_box_section_profile) table_widget.setCellWidget(row, col+1, combo_box_connection_location) # Storing their Object References per each row  combo_box_section_profile_list.append(combo_box_section_profile) combo_box_connection_location_list.append(combo_box_connection_location) # Collectively storing Object References for both of them for all the rows  combo_box_dictionary['Section profile'] = combo_box_section_profile_list combo_box_dictionary['Connection Location'] = combo_box_connection_location_list else: # Logic for adding simple QComboBoxes to the table widget  combo_box_list = [] for row in range(8): combo_box = QtWidgets.QComboBox() combo_box.addItems(value) table_widget.setCellWidget(row, col, combo_box) combo_box_list.append(combo_box) combo_box_dictionary[key] = combo_box_list # --------------------- # More other code below # --------------------- 
Enter fullscreen mode Exit fullscreen mode

Output:
The main problem

Note:

  • If you want to see the whole code you can find it within my GitHub in truss-connection-bolted-gui.
  • If you wish to setup the project within you system, make sure the following things;
    • Use Ubuntu 20.04 LTS version through your VM.
    • Follow the README.md instrustion to install the project environment.
    • And It's recommended to use Pycharm IDE.

I have spent a lot of time search for some answers but, could find one. Is there way to fix this problem ?

Other than my original implementation, I have tried searching for an alternative to the connecting method but couldn't find one...

Then I tried different approaches like storing the object references and then assigning the connecting method to link up the required functionality to the respective QComboBoxes. But, That didn't work out either...

for col, (key, value) in enumerate(selectable_options.items()): combo_box_list = [] for row in range(8): combo_box = QtWidgets.QComboBox() if key == 'Connection Location': value = selectable_options['Connection Location']['Angle'] combo_box.addItems(value) table_widget.setCellWidget(row, col, combo_box) combo_box_list.append(combo_box) combo_box_dictionary[key] = combo_box_list for index, secpro_QComboBox in enumerate(combo_box_dictionary['Section profile']): connloc_QComboBox = combo_box_dictionary['Connection Location'][index] secpro_QComboBox.activated.connect(lambda: connloc_QComboBox.clear() or connloc_QComboBox.addItems( selectable_options['Connection Location']['Angle' if secpro_QComboBox.currentText() in ['Angle', 'Star Angles', 'Back to Back Angles'] else 'Channel'] ) ) 
Enter fullscreen mode Exit fullscreen mode

Output:
The main problem

I don't know what to do, it's giving me the same error. Can anyone figure out what's going on ?

Top comments (1)

Collapse
 
iamwatchdogs profile image
Shamith Nakka

Even though, My question is marked as duplicate and has been closed *( I've applied for review that even though it was a similar case, it's no way near my case )*I have resolved the issue with the advice given by @musicamante in Stackoverflow. Even though it was abstract, I got the idea to implement the same logic differently.

Stackoverflow Comment

In the comment, he suggested that I use an explicit function instead of complex code for creating the dependent QComboBox. So, I have taken the same logic which I initially created and placed it into a method and used it within the current method where the whole logic was supposed to be executed. This method only takes the required data dictionary and returns a pair of dependent QComboBoxes.

Method implementation:

def create_dependent_QComboBox(self, selectable_options): combo_box_section_profile = QtWidgets.QComboBox() combo_box_connection_location = QtWidgets.QComboBox() for item in selectable_options['Section profile']: value = None if item in ['Angles', 'Star Angles', 'Back to Back Angles']: value = selectable_options['Connection Location']['Angle'] elif item in ['Channels', 'Back to Back Channels']: value = selectable_options['Connection Location']['Channel'] else: value = ['Invalid Option'] combo_box_section_profile.addItem(item, value) combo_box_section_profile.currentIndexChanged.connect( lambda: combo_box_connection_location.clear() or combo_box_connection_location.addItems( combo_box_section_profile.currentData() ) ) combo_box_connection_location.addItems( selectable_options['Connection Location']['Angle'] ) return combo_box_section_profile, combo_box_connection_location 
Enter fullscreen mode Exit fullscreen mode

Updated logic:

table_widget = QtWidgets.QTableWidget(self.dockWidgetContents) table_widget.setObjectName(option[0]) table_widget.setRowCount(8) table_widget.setColumnCount(6) combo_box_dictionary = dict() # Just in case, if we need the combo_box object for each option  table_widget.setHorizontalHeaderLabels(list(option[3].keys())) for col, (key, value) in enumerate(option[3].items()): if key == 'Connection Location' or value is None: continue elif key == 'Section profile': combo_box_section_profile_list = [] combo_box_connection_location_list = [] for row in range(8): combo_box_section_profile, combo_box_connection_location = self.create_dependent_QComboBox(option[3]) table_widget.setCellWidget(row, col, combo_box_section_profile) table_widget.setCellWidget(row, col+1, combo_box_connection_location) combo_box_section_profile_list.append(combo_box_section_profile) combo_box_connection_location_list.append(combo_box_connection_location) combo_box_dictionary['Section profile'] = combo_box_section_profile_list combo_box_dictionary['Connection Location'] = combo_box_connection_location_list else: combo_box_list = [] for row in range(8): combo_box = QtWidgets.QComboBox() for item in value: combo_box.addItem(item, value) table_widget.setCellWidget(row, col, combo_box) combo_box_list.append(combo_box) combo_box_dictionary[key] = combo_box_list table_widget.resizeRowsToContents() table_widget.resizeColumnsToContents() 
Enter fullscreen mode Exit fullscreen mode

Source link:

Pull Request: PR#333
Commit: a3ac7e8