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)