193 lines
7.2 KiB
Python
193 lines
7.2 KiB
Python
from PySide6.QtWidgets import QCheckBox, QApplication, QMainWindow, QTableView, QVBoxLayout, QWidget, QPushButton, QAbstractItemView
|
|
from PySide6.QtGui import QStandardItemModel, QStandardItem
|
|
from PySide6.QtCore import QModelIndex, Signal
|
|
import random
|
|
import json
|
|
import os
|
|
import uuid
|
|
|
|
DEBUG_MODE = False
|
|
|
|
class JsonTableWidget(QTableView):
|
|
header_double_clicked = Signal(object)
|
|
def __init__(self, parent=None, dataset_name=""):
|
|
super().__init__(parent)
|
|
self.model = QStandardItemModel()
|
|
# self.model.itemChanged.connect(self.save_data)
|
|
self.setModel(self.model)
|
|
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
|
if dataset_name == "":
|
|
self.dataset_name = str(uuid.uuid4())
|
|
self._save_path = os.path.join(os.path.dirname(__file__), "../../",f"{self.dataset_name}.save.json")
|
|
|
|
# Ajout entêtes de colonne basique
|
|
self.headers = [ ]
|
|
self.refresh_columns_headers()
|
|
self.verticalHeader().sectionDoubleClicked.connect(self.row_double_clicked)
|
|
self.emit_double_click_on_header = False
|
|
|
|
|
|
def refresh_columns_headers(self):
|
|
for col, header in enumerate(self.headers):
|
|
item = QStandardItem(header)
|
|
self.model.setHorizontalHeaderItem(col, item)
|
|
|
|
def add_new_column(self, title= None):
|
|
# Ajouter une nouvelle colonne à la fin du modèle
|
|
num_cols = self.model.columnCount()
|
|
new_header_text = title or f"New Column {num_cols + 1}"
|
|
# print(f"adding new column: {new_header_text}")
|
|
# Créer le nouvel en-tête pour la colonne
|
|
header_item = QStandardItem(new_header_text)
|
|
# Insérer une nouvelle colonne à la fin du modèle
|
|
self.model.insertColumn(num_cols-1)
|
|
header_labels = []
|
|
for i in range(self.model.columnCount()):
|
|
header_item = self.model.horizontalHeaderItem(i)
|
|
if header_item is not None:
|
|
header_labels.append(header_item.text())
|
|
header_labels.insert(len(header_labels) - 1, new_header_text)
|
|
self.model.setHorizontalHeaderLabels(header_labels)
|
|
# self.reset_checkbox()
|
|
|
|
def add_new_row(self, data = None):
|
|
# print(f"tableview state : \nRows -> {self.model.rowCount()}\nCols -> {self.model.columnCount()}")
|
|
valid_data = self.check_incoming_data(data)
|
|
if( valid_data is None):
|
|
# print("No insertion possible. Aborting !!!")
|
|
#Remplir la nouvelle ligne avec des données (par exemple, des nombres aléatoires)
|
|
if DEBUG_MODE:
|
|
# random int insertion
|
|
self.add_new_row({"random": random.randint(1,1000)})
|
|
return
|
|
|
|
# Ajouter une nouvelle colonne à la fin du modèle si elle n'existe pas
|
|
for keys, values in valid_data.items():
|
|
if (keys not in self.headers):
|
|
self.headers.insert(len(self.headers), keys)
|
|
# refraichir les headers
|
|
self.refresh_columns_headers()
|
|
|
|
# Ajouter une nouvelle ligne à la fin du modèle
|
|
num_rows = self.model.rowCount()
|
|
self.model.insertRow(num_rows)
|
|
# print (f"valid data: {valid_data}")
|
|
# Remplir la nouvelle ligne avec les données de datas :
|
|
for col in range(self.model.columnCount()):
|
|
if self.headers[col] not in valid_data.keys():
|
|
# print(f"{self.headers[col]} not in {valid_data.keys()}")
|
|
continue
|
|
if DEBUG_MODE :
|
|
# print(f"key: {self.headers[col]}")
|
|
continue
|
|
item = QStandardItem(str(valid_data[self.headers[col]]))
|
|
self.model.setItem(num_rows, col, item)
|
|
|
|
# delete all rows
|
|
def delete_all(self):
|
|
while self.model.rowCount() > 0:
|
|
self.model.removeRow(0)
|
|
|
|
|
|
def del_row(self, row_id):
|
|
if isinstance(row_id, int):
|
|
self.model.removeRow(row_id)
|
|
elif isinstance(row_id, QModelIndex):
|
|
self.model.removeRow(row_id.row())
|
|
else:
|
|
# print(f"Invalid type {type(row_id)}")
|
|
return
|
|
self.clearSelection()
|
|
|
|
def delete_selected_rows(self):
|
|
selection = self.selectionModel().selectedRows()
|
|
for row in selection:
|
|
# print(f"delete row: {row}")
|
|
self.del_row(row)
|
|
|
|
def check_incoming_data(self, data) -> object:
|
|
if isinstance(data, dict):
|
|
return data
|
|
elif isinstance(data, str):
|
|
try:
|
|
return json.loads(data)
|
|
except json.JSONDecodeError:
|
|
return {"data": data}
|
|
else:
|
|
# print(f"invalid data type : {type(data)}")
|
|
return None
|
|
|
|
def save_data(self, path=None):
|
|
# Récupérez toutes les données du modèle
|
|
data = self.get_all_data()
|
|
if path is None:
|
|
path = self._save_path
|
|
# print(f"data {data}")
|
|
try:
|
|
# Serialisez les données en JSON et sauvegardez-les dans un fichier
|
|
with open(path, 'w') as file:
|
|
try:
|
|
json.dump(data, file, indent=4)
|
|
except Exception as json_e:
|
|
print(f"Json exception {json_e}")
|
|
except Exception as e:
|
|
print(f"Exception {type(e)} while saving data \n\"{e}\"")
|
|
|
|
|
|
|
|
def append_data(self, data):
|
|
try:
|
|
# Serialisez les données en JSON et sauvegardez-les dans un fichier
|
|
with open(self._save_path, 'a') as file:
|
|
json.dump(data, file, indent=4)
|
|
except Exception as e:
|
|
print(f"Exception while appending data {e}")
|
|
|
|
|
|
def get_all_data(self):
|
|
data = []
|
|
for row in range(self.model.rowCount()):
|
|
row_data = {}
|
|
for col in range(self.model.columnCount()):
|
|
item = self.model.item(row, col)
|
|
if item is not None:
|
|
row_data[self.headers[col]] = item.text()
|
|
data.append(row_data)
|
|
return data
|
|
|
|
|
|
|
|
|
|
def row_double_clicked(self, index: int):
|
|
if(not self.emit_double_click_on_header):
|
|
return
|
|
# Copier le contenu de la ligne
|
|
print(f"index = {index}")
|
|
row_data = {}
|
|
|
|
# obtenir le row à l'index
|
|
# obtenir les textes des colonnes pour cette ligne
|
|
for col in range(self.model.columnCount()):
|
|
item = self.model.item(index, col)
|
|
if item is not None:
|
|
data = item.text()
|
|
data = data.replace("'", "\"")
|
|
data = data.replace("False", "false")
|
|
data = data.replace("True", "true")
|
|
try:
|
|
data = json.loads(data)
|
|
print(f"can be json: {data}")
|
|
except Exception as e:
|
|
print(f"not a json: {e} {data}")
|
|
|
|
row_data[self.headers[col]] = data
|
|
|
|
print(f"Row data: {row_data}") # Ajouté pour déboguer
|
|
# obtenir les textes des colonnes
|
|
|
|
#remplir le dictionnaire data avec pour clefs les noms des colonnes et pour valeurs les élément du row
|
|
|
|
self.header_double_clicked.emit(row_data)
|
|
|
|
|