I activated Drag and Drop mode for my QlistWidget, so I need to catch a signal from IndexesMoved to rewrite changes in settings file. But when items moved, the event handler doesn't call my function. I can't understand why? I thought every widget got an index when it added into widgetlist, but I guess they don't. So if there are no indexes, nothing have to change. This is my code ....
import json
import os.path
from PySide6.QtCore import QSize, QRect
from PySide6.QtWidgets import QGroupBox, QPushButton, QWidget, QListWidget, QListWidgetItem, QAbstractItemView
from windows import ClientCreationWindow
from windows.custom_wigets import ClientListWidget
class ChooseClientWindow(QWidget):
def __init__(self):
super(ChooseClientWindow, self).__init__()
self.setWindowTitle('app name')
self.clients_group_box = QGroupBox(self)
self.clients_group_box.setObjectName(u'clients_group_box')
self.clients_list = QListWidget(self.clients_group_box)
self.clients_list.setObjectName(u'clients_list')
self.custom_widgets_dict = None
self.add_button = QPushButton(self.clients_group_box)
self.add_button.setObjectName(u'add_button')
self.del_button = QPushButton(self.clients_group_box)
self.del_button.setObjectName(u'del_button')
self.choose_button = QPushButton(self.clients_group_box)
self.choose_button.setObjectName(u'choose_button')
# MainWindow object
self.create_client_window = None
self.geometry_init()
def geometry_init(self):
self.setMinimumSize(QSize(390, 190))
self.setMaximumSize(QSize(390, 190))
self.group_boxes_init()
self.lists_init()
self.buttons_init()
def group_boxes_init(self):
self.clients_group_box.setGeometry(QRect(10, 10, 370, 160))
self.clients_group_box.setTitle('Connections')
def buttons_init(self):
self.add_button.setGeometry(QRect(250, 30, 110, 30))
self.add_button.setText('Create')
self.add_button.clicked.connect(self.add_client)
self.del_button.setGeometry(QRect(250, 70, 110, 30))
self.del_button.setText('Delete')
self.del_button.clicked.connect(self.delete_client)
self.choose_button.setGeometry(QRect(250, 110, 110, 30))
self.choose_button.setText('Choose')
self.choose_button.clicked.connect(self.choose_client)
def lists_init(self):
self.clients_list.setGeometry(QRect(10, 30, 230, 120))
# code that fills list
self.clients_list.doubleClicked.connect(self.choose_client)
self.clients_list.setDragDropMode(QAbstractItemView.InternalMove)
self.clients_list.indexesMoved.connect(self.widget_position_changed)
def widget_position_changed(self):
settings: dict[int, str] = {}
for index, widget in enumerate(self.clients_list.items(ClientListWidget)):
settings[index] = widget.name_label.text()
json.dump(settings, open('settings.txt', 'w'))
You misinterpreted the documentation about
indexesMoved:(emphasis mine)
While the normal usage of QListView (from which QListWidget directly inherits) is to vertically display (the visual order) items in the same order they are in the model (the logical order), its primary purpose is just to show objects that are part of a mono dimensional list.
This is a common misconception: how items are visually shown in a view is completely arbitrary, and only depends on how the view is implemented. The model (what contains the actual logical order) is what actually matters. Remember that QListWidget (like QTableWidget and QTreeWidget) is just a higher level implementation of a view that "embeds" a model: in these higher level classes, models and views are quite interconnected, but the underlying Qt model/view concepts remain.
In the case of QListView, it's just its default behavior that shows those items visually like they are logically ordered, but that's it; this is clearly explained in its documentation: in fact, items can be shown in very different ways in a QListView, such as a grid or any arbitrary order, just like an "icon view"; think about file managers: while they allow moving the icons around, their visual position won't change their "order" in the file system. In fact, Qt uses QListView in icon mode when showing non-native QFileDialogs.
Properties like
viewModeandmovementwill make drag and drop feature only work for visual display and will never affect the logical order.So, that signal has absolutely nothing to do with reordering, but it's only related to the visible position of items in the view: when the user moves an item (such as an icon representing a file) in a different position within the view. When that happens, the logical order of items is unchanged, only the visual one is. Fundamentally speaking, it's just like dragging the sections in a header of a QTableView: doing it won't change the order of the rows or columns in the model, just their visual position (that's why QHeaderView provides functions like
logicalIndex()andvisualIndex()).If you need to keep track of changes in the logical order of items, then you need to check that against the model, not the view: in this specific case, you need to connect to the
rowsMovedsignal:Note that the
targetargument is the insertion index before the selected item has been moved.If you have
a,bandcand you want to moveaafterb, thetargetwill be2, not1, because that's the position after b before any movement was applied.If you rebuild the json data from scratch every time, that is not a problem, as you can completely ignore the arguments, but in case you need to keep persistent ordering outside of the list widget, you must always consider that when an item is moved after its position.
Practically, this means that movements in a model are done by "inserting" the item in the new position before removing it from the old.
Finally, you should really use layout managers.