Code commit
This commit is contained in:
1 parent
e68f09aeaa
commit
0411f84062
15 files changed
+1763
No files matched your search
Whitespace-only changes.
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# GUI/__init__.py
|
||||
from .main_window import MainWindow
|
||||
|
||||
|
||||
__all__ = ['MainWindow']
|
||||
@@ -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))
|
||||
Reference in new issue
Block a user