1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
| #!/usr/bin/env python3
# (pyqt5.15.2 / python3.9)
import sys
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtCore import Qt, QModelIndex, QItemSelectionModel, QTimer
from PyQt5.QtWidgets import (
QMainWindow,
QApplication,
QListView,
QAbstractItemView,
)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
elements = [
{'name' : 'bob', 'state' : True},
{'name' : 'alice', 'state' : True},
{'name' : 'marc', 'state' : False},
{'name' : 'eve', 'state' : True}
]
listeWdg = OrderNChooseWdg(elements)
self.setCentralWidget(listeWdg)
def keyPressEvent(self, e):
if e.key() == Qt.Key_Escape:
QApplication.quit()
class OrderNChooseWdg(QListView):
"""Permet d'ordonner une liste par glisser-déposer ou shift + flèches du clavier
**A la souris**::
mousePressEvent -> set_old_row -> obtention de l'"ancienne" ligne (self.old_row)
rowsInserted -> get_inserted_row (obtention de la nouvelle ligne (self.new_row)) -> update_selection -> met en surbrillance la ligne d'indice self.new_row
**Au clavier**::
keyPressEvent -> self.new_row = ancienne +/- 1 -> update_selection
L'utilisation des QTimer.singleShot permet de récupérer à posteriori
des valeurs modifiées par un événement (qui déclenche lui même le timer...)
"""
def __init__(self, elements):
super().__init__()
self.setDragDropMode(QAbstractItemView.InternalMove)
self.setDefaultDropAction(Qt.MoveAction)
self.setDragDropOverwriteMode(False)
self.setAcceptDrops(True)
self.setDropIndicatorShown(True)
self.setDragEnabled(True)
self.is_shift_pressed = False
self.old_row = -1000
self.model = QStandardItemModel(self)
self.model.rowsInserted.connect(self.get_inserted_row)
self.setModel(self.model)
self.setSelectionMode(QAbstractItemView.SingleSelection)
for elt in elements:
self.add_item(elt['name'], elt['state'])
###################### Au clavier ################################
def keyReleaseEvent(self, e):
if e.key() == Qt.Key_Shift:
self.is_shift_pressed = False
def keyPressEvent(self, e):
if e.key() == Qt.Key_Shift:
self.is_shift_pressed = True
# Move selected QStandardItem up or down in QListView
indexes = self.selectionModel().selectedIndexes()
if indexes:
row = indexes[0].row()
if self.is_shift_pressed:
if e.key() == Qt.Key_Up:
if row > 0:
item = self.model.takeRow(row)
self.model.insertRow(row-1, item)
self.new_row = row - 1
QTimer.singleShot(1, self.update_selection)
elif e.key() == Qt.Key_Down:
if row < self.model.rowCount() - 1:
item = self.model.takeRow(row)
self.model.insertRow(row+1, item)
self.new_row = row + 1
QTimer.singleShot(1, self.update_selection)
QListView.keyPressEvent(self, e)
################ A la souris ##########################
def get_inserted_row(self, parent, row_inserted, row_end):
"""Get row where item is inserted"""
self.new_row = row_inserted # save index to update selection
if self.old_row < self.new_row:
self.new_row += -1 # don't count current item, it will be removed
QTimer.singleShot(1, self.update_selection)
def mousePressEvent(self, e):
"""Trigger 'set_old_row' with a small delay so new selection is effective"""
# Save index (after small delay), in case of drag'n'drop
QTimer.singleShot(1, self.set_old_row)
QListView.mousePressEvent(self, e)
def set_old_row(self):
"""Save currently selected item row (on mouse event) before draging it"""
self.old_row = self.selectionModel().selectedIndexes()[0].row()
############## Mise à jour selection ##################################
def update_selection(self):
"""Highlight item that have been moved given its new row"""
index = self.model.index(self.new_row, 0)
self.selectionModel().setCurrentIndex(index, QItemSelectionModel.ClearAndSelect)
self.scrollTo(index)
########################## Autre ####################################
def add_item(self, name, state):
"""Fill list with values"""
item = QStandardItem(name)
item.setCheckable(True)
item.setCheckState(2*state)
item.setDragEnabled(True)
item.setDropEnabled(False)
item.setToolTip("Drag and drop to change tab order")
self.model.appendRow(item)
if __name__ == "__main__":
app = QApplication(sys.argv)
ex = MainWindow()
ex.show()
sys.exit(app.exec_()) |
Partager