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 167 168 169 170 171 172 173 174
| #!/usr/bin/env python
# coding: utf-8
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys
class QtData(QWidget):
def __init__(self, data, *args, **kwargs):
super().__init__(*args, **kwargs)
self.resize(600, 300)
# La zone de sélection
self.__tableChoice=QTableWidget(0, 0, parent=self, itemChanged=self.__slotFiltre)
self.__tableChoice.setRowCount(1)
self.__tableChoice.setColumnCount(len(data[0]))
# L'éditeur de la zone de sélection
self.__tableChoice.setItemDelegate(QtData.__QtEditor(parent=self))
# La zone de datas
self.__tableData=QTableWidget(0, 0, parent=self)
self.__tableData.setColumnCount(len(data[0]))
self.__tableData.setSortingEnabled(True)
# Positionnement principal avec correction de l'espace inutile
mainLayout=QVBoxLayout(self)
mainLayout.setSpacing(0)
mainLayout.setContentsMargins(0, 0, 0, 0)
# Ajustement de la taille de la première table
self.__tableChoice.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
self.__tableChoice.setMaximumHeight(self.__tableChoice.verticalHeader().sectionSize(0) + self.__tableChoice.horizontalHeader().height())
# Ajout des tables avec gestion du stretch
mainLayout.addWidget(self.__tableChoice)
mainLayout.addWidget(self.__tableData, 1)
# Remplissage QTableWidget
self.__loadData(data)
# __init__()
# Remplissage table
def __loadData(self, data):
# Le tableau est initialisé
self.__tableData.blockSignals(True)
self.__tableData.clearContents()
# Remplissage
for (row, d) in enumerate(data):
for (col, value) in enumerate(d):
item=QTableWidgetItem(str(value))
item.setData(Qt.UserRole, str(value))
self.__tableData.setRowCount(row+1)
self.__tableData.setItem(row, col, item)
# for
# for
self.__tableData.blockSignals(False)
# Chargement zone de recherche (qui récupèrera les données du tableau)
self.__prepareRecherche()
# __loadData()
# Chargement zone de recherche
def __prepareRecherche(self):
# Création des valeurs associées à chaque colonne de la zone de recherche
values=tuple(set() for i in range(self.__tableChoice.columnCount()))
for row in range(self.__tableData.rowCount()):
for col in range(self.__tableData.columnCount()):
values[col].add(self.__tableData.item(row, col).data(Qt.DisplayRole))
# Remplissage des données de la table des choix
self.__tableChoice.blockSignals(True)
self.__tableChoice.clearContents()
for (col, v) in enumerate(values):
item=QTableWidgetItem()
item.setData(
Qt.UserRole,
{"values" : tuple(sorted(v)), "find" : None, "idx" : 0},
)
self.__tableChoice.setItem(0, col, item)
# for
self.__tableChoice.blockSignals(False)
# __prepareRecherche()
# Slot filtre
def __slotFiltre(self, *args, **kwargs):
# Balayage des lignes
for row in range(self.__tableData.rowCount()):
# Balayage des colonnes
for col in range(self.__tableData.columnCount()):
# Récupération item cherché
find=self.__tableChoice.item(0, col).data(Qt.UserRole)["find"]
# Si l'item ne correspond pas
if find is not None and self.__tableData.item(row, col).data(Qt.UserRole) != find:
# On cache la ligne et on arrête
self.__tableData.hideRow(row)
break
# if
else: # Si pas trouvé de "non correspondance"
# On affiche la ligne
self.__tableData.showRow(row)
# for
# for
# __slotFiltre
# Editeur associé au tableWidget
class __QtEditor(QItemDelegate):
# Création de l'éditeur (méthode surchargée)
def createEditor(self, parent, option, index):
# Si on n'est pas sur ligne 0 (sécurité)
if index.row() != 0:
# Aucun éditeur
return None
# Création d'un menu déroulant
return QComboBox(parent=parent)
# createEditor()
# Positionne valeur dans zone d'édition
def setEditorData(self, editor, index):
# S'il n'y a pas d'éditeur
if editor is None: return
# Obtenir la largeur de la colonne et ajuster celle du QComboBox
editor.setMinimumWidth(index.model().index(0, index.column()).tableWidget().columnWidth(index.column()))
# Récupération informations associées à colonne
indexRef=index.sibling(0, index.column())
data=indexRef.model().data(indexRef, Qt.UserRole)
# Remplissage du menu déroulant (avec une valeur nulle pour annuler la recherche)
for d in (None,) + data["values"]:
editor.addItem("[%s]" % d if d is not None else "", d)
editor.setCurrentIndex(data["idx"])
# setEditorData()
# Enregistre valeur dans modèle
def setModelData(self, editor, model, index):
# S'il n'y a pas d'éditeur, inutile de continuer
if editor is None: return
# Récupération informations associées à colonne
indexRef=index.sibling(0, index.column())
data=indexRef.model().data(indexRef, Qt.UserRole)
# Enregistrement choix utilisateur dans modèle
idx=editor.currentIndex()
val=editor.itemData(idx)
indexRef.model().setData(
indexRef,
{"values" : data["values"], "find" : val, "idx" : idx},
Qt.UserRole,
)
# Réaffichage menu déroulant
indexRef.model().setData(
indexRef,
"[%s]" % val if val is not None else "",
Qt.DisplayRole,
)
# setModelData()
# class __QtEditor
# class QtData
# Programme principal
if __name__ == "__main__":
import random
app=QApplication(sys.argv)
fen=QtData([[random.randint(1, 10) for i in range(5)] for j in range(10)])
fen.show()
sys.exit(app.exec_())
# if |
Partager