Code commit

This commit is contained in:
ange committed 2026-05-15 17:27:26 +02:00
1 parent e68f09aeaa
commit 0411f84062
15 files changed
+1763

No files matched your search

View File
Whitespace-only changes.
+192
View File
@@ -0,0 +1,192 @@
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)
+75
View File
@@ -0,0 +1,75 @@
from PySide6.QtWidgets import QCheckBox, QLabel, QVBoxLayout, QHBoxLayout, QWidget, QPushButton
# from PySide6.QtGui import QStandardItemModel, QStandardItem
# from PySide6.QtCore import QModelIndex
from GUI.Widgets.json_table_widget import JsonTableWidget
# import random
# import json
class JsonViewer(QWidget):
def __init__(self, title="JsonViewer", parent=None, auto_save=False):
super().__init__(parent)
self._autosave = auto_save
self.main_layout = QVBoxLayout()
self.top_viewer_layout = QHBoxLayout()
self.bottom_viewer_layout = QHBoxLayout()
self.title_widget = QLabel(title)
self.top_viewer_layout.addWidget(self.title_widget)
self.auto_save_checkbox = QCheckBox("AutoSave", self)
self.auto_save_checkbox.setChecked(self._autosave)
self.auto_save_checkbox.stateChanged.connect(self.auto_save_state_changed)
self.top_viewer_layout.addWidget(self.auto_save_checkbox)
self.viewer = JsonTableWidget(self)
self.delete_button = QPushButton("Delete", self)
self.delete_button.clicked.connect(self.delete_rows)
self.bottom_viewer_layout.addWidget(self.delete_button)
self.clear_button = QPushButton("Clear", self)
self.clear_button.clicked.connect(self.delete_all)
self.bottom_viewer_layout.addWidget(self.clear_button)
self.top_container = QWidget(self)
self.bottom_container = QWidget(self)
self.top_container.setLayout(self.top_viewer_layout)
self.bottom_container.setLayout(self.bottom_viewer_layout)
self.main_layout.addWidget(self.top_container)
self.main_layout.addWidget(self.viewer)
self.main_layout.addWidget(self.bottom_container)
self.setLayout(self.main_layout)
def delete_rows(self, path=None):
self.viewer.delete_selected_rows()
if (self._autosave):
self.viewer.save_data(path)
print("Needed deletion")
def auto_save_state_changed(self):
print(f"autosave : {not self._autosave}")
self._autosave = not self._autosave
def add_row(self, data, path=None):
self.viewer.add_new_row(data)
if (self._autosave):
self.viewer.save_data(path)
# delete all rows
def delete_all(self):
self.viewer.delete_all()
pass
def set_emit_double_click_on_header(self, emit: bool) -> None:
self.viewer.emit_double_click_on_header = emit
+5
View File
@@ -0,0 +1,5 @@
# GUI/__init__.py
from .main_window import MainWindow
__all__ = ['MainWindow']
+120
View File
@@ -0,0 +1,120 @@
# main_window.py
from PySide6.QtWidgets import (
QMainWindow,
QTextEdit,
QPushButton,
QVBoxLayout,
QWidget,
QLabel,
QStatusBar,
QHBoxLayout,
QCheckBox,
)
from PySide6.QtCore import Qt
from GUI.Widgets.json_viewer import JsonViewer
from Core.UnrealApi.unreal_api import (
UnrealApi,
) # Assurez-vous d'importer correctement la classe UnrealApi
import json
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("WebSocket JSON Sender")
self.setGeometry(100, 100, 1000, 900)
self.statusBar = QStatusBar()
self.setStatusBar(self.statusBar)
self.statusBar.showMessage("[Disconnected]")
layout = QVBoxLayout()
self.json_input = QTextEdit(self)
self.json_input.setPlaceholderText("Entrez le JSON ici...")
layout.addWidget(self.json_input)
self.send_button = QPushButton("Envoyer", self)
self.send_button.clicked.connect(self.send_json_message)
layout.addWidget(self.send_button)
self.ping_button = QPushButton("Ping", self)
self.ping_button.clicked.connect(self.send_ping)
layout.addWidget(self.ping_button)
self.sent_display = JsonViewer("Sent to server", self)
self.sent_display.set_emit_double_click_on_header(True)
self.sent_display.viewer.header_double_clicked.connect(self.sent_display_header_double_clicked)
layout.addWidget(self.sent_display)
self.server_layout = QHBoxLayout()
self.server_widget = QWidget()
self.response_display = JsonViewer("Received from server", self, True)
self.filter_avalanche_checkbox = QCheckBox("Filter Avalanche", self)
# default to checked
self.filter_avalanche_checkbox.setChecked(True)
self.filter_avalanche_checkbox.stateChanged.connect(self.filter_avalanche)
self.server_layout.addWidget(self.response_display)
self.server_layout.addWidget(self.filter_avalanche_checkbox)
self.server_widget.setLayout(self.server_layout)
layout.addWidget(self.server_widget)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
self.unreal_api = UnrealApi(
"ws://localhost:30021", self
) # Assurez-vous que le second argument est correct
self.unreal_api.set_filtered(True)
# Connecter les signaux aux slots
self.unreal_api.message_received.connect(self.on_message_received)
self.unreal_api.message_sent.connect(self.on_message_sent)
self.unreal_api.ws_connected.connect(self.ws_connected)
self.unreal_api.ws_connect()
def send_json_message(self):
message_text = self.json_input.toPlainText().strip()
print(f"About to send {message_text}")
if message_text:
self.unreal_api.send_message(message_text)
# self.sent_display.add_row(data=message_text)
def on_message_received(self, message):
# print(f"MainWindow received message: {message}")
self.response_display.add_row(data=message)
def on_message_sent(self, sent) -> None:
self.sent_display.add_row(data=sent)
def ws_connected(self, connected: bool) -> None:
if connected:
self.statusBar.showMessage("[Connected]")
else:
self.statusBar.showMessage("[Disconnected]")
def closeEvent(self, event):
self.unreal_api.close_websocket() # Assurez-vous de fermer correctement le websocket
event.accept()
def filter_avalanche(self, state):
print(f"filter_avalanche state: {state}")
if (state == 2):
print("changing state to True")
self.unreal_api.set_filtered(True)
elif (state == 0):
print("changing state to False")
self.unreal_api.set_filtered(False)
def send_ping(self):
print("sending ping to unreal")
self.unreal_api.send_ping()
def sent_display_header_double_clicked(self, data):
self.json_input.clear()
self.json_input.append(json.dumps(data, indent=2))