Getting Row from ListWidget
-
I'm trying to figure out how to get the selected row from a list widget. Currently I have a method that will get me the text of the row but I need to find the actual position. (Basically I have a dictionary with duplicate values but unique keys the list widget is displaying the values but I need to know which unique key which is handled by position) Here's what I've got so far:
import sys from PySide2 import QtCore, QtGui, QtWidgets # Create Test List/Dictionary list_dict = {1: "First Label", 2: "Second Label", 3: "First Label"} list_list = [1, 2, 3] #Create Widgets/Layout class Widget(QtWidgets.QWidget): def __init__(self, parent = None): super(Widget, self).__init__(parent) self.listwidget = QtWidgets.QListWidget() self.lineedit = QtWidgets.QLineEdit("Nothing clicked") lay = QtWidgets.QVBoxLayout(self) lay.addWidget(self.listwidget) lay.addWidget(self.lineedit) # Populate List with Values from Dictionary for e in list_list: value = list_dict.get(e) it = QtWidgets.QListWidgetItem() it.setData(QtCore.Qt.DisplayRole, value) self.listwidget.addItem(it) self.listwidget.itemClicked.connect(self.on_item_clicked) @QtCore.Slot(QtWidgets.QListWidgetItem) def on_item_clicked(self, item): # This gets the text from the list box key = item.data(QtCore.Qt.DisplayRole) self.lineedit.setText(key) def main(): app = QtWidgets.QApplication(sys.argv) window = Widget() window.show() sys.exit(app.exec_()) if __name__ == "__main__": main()
I've tried changing key = to be row.data, self.row, self.currentrow. I've also tried changing the def on_item_clicked to contain self and row instead of self and item, but I haven't figured out any way to get the row passed from the listwidget into my function.
Note: Currently I'm using the lineedit box to report out what is passed into key just to help me check the code.
-
The currently selected rows can be retrieved through the selection model
-
That definitely got me closer. I was able to find this site that gives me the python syntax for the selection model: https://doc.qt.io/qtforpython/PySide2/QtCore/QItemSelectionModel.html, but I'm still having trouble getting it to work in the code.
I edited the on_item_clicked(self,item) function to now have
active_item = QtCore.QItemSelectionModel.selectedRows self.lineedit.setText(str(active_item))
But the value returned by active item is:
<method 'selectedRows' of 'PySide2.QtCore.QItemSelectionModel' objects>
How do I actually get the value? I tried wrapping it in item.data like I had previously with the display role but that gives me an error.
-
That definitely got me closer. I was able to find this site that gives me the python syntax for the selection model: https://doc.qt.io/qtforpython/PySide2/QtCore/QItemSelectionModel.html, but I'm still having trouble getting it to work in the code.
I edited the on_item_clicked(self,item) function to now have
active_item = QtCore.QItemSelectionModel.selectedRows self.lineedit.setText(str(active_item))
But the value returned by active item is:
<method 'selectedRows' of 'PySide2.QtCore.QItemSelectionModel' objects>
How do I actually get the value? I tried wrapping it in item.data like I had previously with the display role but that gives me an error.
@BD4L
You need to get the selection model of theQListWidget
instance you have. So:self.listwidget.selectionModel().selectedRows()
would give you a list of
QModelIndex
es into your model.Depending on what you want, see e.g. https://doc.qt.io/qt-5/qlistwidget.html#selectedItems which gives you a list of
QListWidgetItem
s without going via indexes.Your "but I need to know which unique key which is handled by position" sounds a bit dodgy, but up to you. Another way of dealing with such an issue is to store the necessary discriminatory item (your unique key) in the list item data, so that the value you get back includes whatever is needed to distinguish, rather than relying on looking at row numbers. Which is then more flexible, e.g. if rows get reordered.
-
@Denni-0 That code worked fine with Pyside2, but it ran into the same issue I had earlier where it gives me the text from the row but doesn't give me the row ID. I am grateful for a lot of your suggestions there, a lot of the code I'm using is pulled together from tutorials online etc. and I'm still trying to pick apart what it all means. So some clarification on when not to do things is great.
@JonB I tried using that line of code but got the same value I did before of <method 'selectedRows' of 'PySide2.QtCore.QItemSelectionModel' objects>. I'd be interested in learning another way to handle the keys but I need to give you a better description of the full data I'm dealing with. I have a dictionary with the following structure:
countries_dict = {1 : {"name" : "United Kingdom", "abbrev" : "UK", "pop" : 66.44, "cities" : { 1 : {"name" : "London", "pop" : 8.9}, 2 : {"name" : "York", "pop" : 0.04}} , 2 : {"name" : "United States", "abbrev" : "US", "pop" : 327.2, "cities" : { 1 : {"name" : "Springfield,", "pop" : 0.16, "state" : "MO"}, 2 : {"name" : "Springfield", "pop" : 0.11, "state" : "IL"}}}
Sorry I know the formatting isn't great. I also can't use the actual data from my dictionary and this was the closest idea I could come up with to replicate it. Basically I'm dealing with nested dictionaries that are identified by unique keys because the name for each item can be duplicated (see US example above where both cities are named Springfield.
I'm still very new to QT so I've been trying to build up my document slowly but what I will need to have eventually is a list box containing all of the first level items (in this case it would be United Kingdom and United States). When one of those is selected from the list box I will need other fields to populate (in this example it would be a text box for population, and another list box for cities - including only the cities from that country). Then when they select a city from the second list box it would populate another text box (city population) and a combo box (state).
Users will need to be able to add countries and cities dynamically so I can't really make each country its own dictionary, but the order that they are doesn't matter to the users so I have it as a fixed order based on when each item was added to the dictionary (the list boxes don't need to allow for items to be reordered). Which is why my current plan is to just extract the row index from the selected item match that to the list index used to populate the list and then look up the matching dictionary item by the ID stored in the list at that location.
The current dictionary structure is working really well for the program from a command line standpoint (I can easily display, manipulate, add, delete and modify data) but now we need to make it accessible to users which means a GUI which is something I'm not very familiar with unfortunately.
So if there's a way to store the unique id in the list element but have it not be visible to the user, that would be amazing and get me closer to how the command line version functions, but this is the closest idea I've had so far to dealing with the data structure I have.
Thanks again to everyone for their help so far.
-
That would be awesome, thanks. Just so you're aware, my current plan was to figure out how to get the selected row from the listbox and then compare that to the the index of the list list that I'm using to drive loading the list box (ListList in the code). Which would then return the unique key which I can store and use to access the correct section of the dictionary both at that point and when a selection is made in the child listbox.
The way I'm currently handling this is to print the unique id and name of each country to the command line and ask users to enter the ID key which I then use to retrieve the data. I was hoping, as part of moving to the gui version) to make those unique keys invisible so users just have to select the name of what they're looking for. I have gotten it to work by concatenating the key and value from the dictionary displaying that as a string in the list box, then getting the text from the list box removing the unique ID and accessing the dictionary directly, but that's not ideal.
Thanks again for all your help.
-
@Denni-0 That code worked fine with Pyside2, but it ran into the same issue I had earlier where it gives me the text from the row but doesn't give me the row ID. I am grateful for a lot of your suggestions there, a lot of the code I'm using is pulled together from tutorials online etc. and I'm still trying to pick apart what it all means. So some clarification on when not to do things is great.
@JonB I tried using that line of code but got the same value I did before of <method 'selectedRows' of 'PySide2.QtCore.QItemSelectionModel' objects>. I'd be interested in learning another way to handle the keys but I need to give you a better description of the full data I'm dealing with. I have a dictionary with the following structure:
countries_dict = {1 : {"name" : "United Kingdom", "abbrev" : "UK", "pop" : 66.44, "cities" : { 1 : {"name" : "London", "pop" : 8.9}, 2 : {"name" : "York", "pop" : 0.04}} , 2 : {"name" : "United States", "abbrev" : "US", "pop" : 327.2, "cities" : { 1 : {"name" : "Springfield,", "pop" : 0.16, "state" : "MO"}, 2 : {"name" : "Springfield", "pop" : 0.11, "state" : "IL"}}}
Sorry I know the formatting isn't great. I also can't use the actual data from my dictionary and this was the closest idea I could come up with to replicate it. Basically I'm dealing with nested dictionaries that are identified by unique keys because the name for each item can be duplicated (see US example above where both cities are named Springfield.
I'm still very new to QT so I've been trying to build up my document slowly but what I will need to have eventually is a list box containing all of the first level items (in this case it would be United Kingdom and United States). When one of those is selected from the list box I will need other fields to populate (in this example it would be a text box for population, and another list box for cities - including only the cities from that country). Then when they select a city from the second list box it would populate another text box (city population) and a combo box (state).
Users will need to be able to add countries and cities dynamically so I can't really make each country its own dictionary, but the order that they are doesn't matter to the users so I have it as a fixed order based on when each item was added to the dictionary (the list boxes don't need to allow for items to be reordered). Which is why my current plan is to just extract the row index from the selected item match that to the list index used to populate the list and then look up the matching dictionary item by the ID stored in the list at that location.
The current dictionary structure is working really well for the program from a command line standpoint (I can easily display, manipulate, add, delete and modify data) but now we need to make it accessible to users which means a GUI which is something I'm not very familiar with unfortunately.
So if there's a way to store the unique id in the list element but have it not be visible to the user, that would be amazing and get me closer to how the command line version functions, but this is the closest idea I've had so far to dealing with the data structure I have.
Thanks again to everyone for their help so far.
@BD4L said in Getting Row from ListWidget:
@JonB I tried using that line of code but got the same value I did before of <method 'selectedRows' of 'PySide2.QtCore.QItemSelectionModel' objects>.
I don't understand, and you should show the actual code you tried. What I wrote should work. The message you quote should --- as in your original code --- result from trying to output
self.listwidget.selectionModel().selectedRows
rather than what I wrote, which is:
self.listwidget.selectionModel().selectedRows()
Did you copy what I wrote, or did you do your own thing as per the first example above instead of the second?
-
Hey friends, you guys are awesome thanks. Here's my replies
@JonB I copied the text directly into my slot so:
@QtCore.Slot(QtWidgets.QListWidgetItem) def on_item_clicked(self, item): active_item = QtCore.QItemSelectionModel.selectedIndexes self.lineedit.setText(str(active_item))
became:
@QtCore.Slot(QtWidgets.QListWidgetItem) def on_item_clicked(self, item): active_item = self.listwidget.selectionModel().selectedRows() self.lineedit.setText(str(active_item))
I'm not sure if I put that in the wrong spot or why it didn't work, but it still gave me the same error as before. However the code from @Denni-0 worked so I now have the info I need.
Denni-O, I just had a question about why you did something in your changes. I get adding the second line display to show the second piece of information but why did you put both of the line edit items into an HBox and then wrap that inside the VBox. Would it have worked just to add both of them into the VBox layout?
-
Hey friends, you guys are awesome thanks. Here's my replies
@JonB I copied the text directly into my slot so:
@QtCore.Slot(QtWidgets.QListWidgetItem) def on_item_clicked(self, item): active_item = QtCore.QItemSelectionModel.selectedIndexes self.lineedit.setText(str(active_item))
became:
@QtCore.Slot(QtWidgets.QListWidgetItem) def on_item_clicked(self, item): active_item = self.listwidget.selectionModel().selectedRows() self.lineedit.setText(str(active_item))
I'm not sure if I put that in the wrong spot or why it didn't work, but it still gave me the same error as before. However the code from @Denni-0 worked so I now have the info I need.
Denni-O, I just had a question about why you did something in your changes. I get adding the second line display to show the second piece of information but why did you put both of the line edit items into an HBox and then wrap that inside the VBox. Would it have worked just to add both of them into the VBox layout?
but it still gave me the same error as before
So to quote you from before:
active_item = QtCore.QItemSelectionModel.selectedRows
self.lineedit.setText(str(active_item))
But the value returned by active item is:<method 'selectedRows' of 'PySide2.QtCore.QItemSelectionModel' objects>
You are saying that it shows the same string, with
method 'selectedRows'
in it, when you replace the first line with:active_item = self.listwidget.selectionModel().selectedRows()
You get the same
str(active_item)
result string with"method"
in it in the second case, do you?