linting fix

This commit is contained in:
Saifeddine ALOUI
2025-01-22 00:40:39 +01:00
parent f5fd8d5eac
commit 6db8b5bf79

View File

@@ -8,6 +8,7 @@ Version: 2.2
from pathlib import Path from pathlib import Path
from typing import Optional, Tuple, Dict, List, Any from typing import Optional, Tuple, Dict, List, Any
import pipmaster as pm import pipmaster as pm
# Install all required dependencies # Install all required dependencies
REQUIRED_PACKAGES = [ REQUIRED_PACKAGES = [
"PyQt5", "PyQt5",
@@ -18,10 +19,9 @@ REQUIRED_PACKAGES = [
"networkx", "networkx",
"matplotlib", "matplotlib",
"python-louvain", "python-louvain",
"ascii_colors" "ascii_colors",
] ]
from ascii_colors import ASCIIColors, trace_exception
def setup_dependencies(): def setup_dependencies():
""" """
@@ -32,6 +32,7 @@ def setup_dependencies():
print(f"Installing {package}...") print(f"Installing {package}...")
pm.install(package) pm.install(package)
# Install dependencies # Install dependencies
setup_dependencies() setup_dependencies()
@@ -40,18 +41,32 @@ import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import community import community
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QApplication,
QHBoxLayout, QPushButton, QFileDialog, QLabel, QMainWindow,
QMessageBox, QSpinBox, QComboBox, QCheckBox, QWidget,
QTableWidget, QTableWidgetItem, QSplitter, QDockWidget, QVBoxLayout,
QTextEdit QHBoxLayout,
QPushButton,
QFileDialog,
QLabel,
QMessageBox,
QSpinBox,
QComboBox,
QCheckBox,
QTableWidget,
QTableWidgetItem,
QSplitter,
QDockWidget,
QTextEdit,
) )
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
import pyqtgraph.opengl as gl import pyqtgraph.opengl as gl
from ascii_colors import trace_exception
class Point: class Point:
"""Simple point class to handle coordinates""" """Simple point class to handle coordinates"""
def __init__(self, x: float, y: float): def __init__(self, x: float, y: float):
self.x = x self.x = x
self.y = y self.y = y
@@ -59,6 +74,7 @@ class Point:
class NodeState: class NodeState:
"""Data class for node visual state""" """Data class for node visual state"""
NORMAL_SCALE = 1.0 NORMAL_SCALE = 1.0
HOVER_SCALE = 1.2 HOVER_SCALE = 1.2
SELECTED_SCALE = 1.3 SELECTED_SCALE = 1.3
@@ -76,8 +92,15 @@ class NodeState:
class Node3D: class Node3D:
"""Class representing a 3D node in the graph""" """Class representing a 3D node in the graph"""
def __init__(self, position: np.ndarray, color: Tuple[float, float, float, float],
label: str, node_type: str, size: float): def __init__(
self,
position: np.ndarray,
color: Tuple[float, float, float, float],
label: str,
node_type: str,
size: float,
):
self.position = position self.position = position
self.base_color = color self.base_color = color
self.color = color self.color = color
@@ -119,12 +142,13 @@ class Node3D:
"""Update node visual appearance""" """Update node visual appearance"""
if self.mesh_item: if self.mesh_item:
self.mesh_item.setData( self.mesh_item.setData(
color=np.array([self.color]), color=np.array([self.color]), size=np.array([self.size * scale * 5])
size=np.array([self.size * scale * 5])
) )
class NodeDetailsWidget(QWidget): class NodeDetailsWidget(QWidget):
"""Widget to display node details""" """Widget to display node details"""
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.init_ui() self.init_ui()
@@ -161,13 +185,14 @@ class NodeDetailsWidget(QWidget):
for idx, (neighbor, edge_data) in enumerate(connections.items()): for idx, (neighbor, edge_data) in enumerate(connections.items()):
self.connections.setItem(idx, 0, QTableWidgetItem(str(neighbor))) self.connections.setItem(idx, 0, QTableWidgetItem(str(neighbor)))
self.connections.setItem( self.connections.setItem(
idx, 1, idx, 1, QTableWidgetItem(edge_data.get("relationship", "unknown"))
QTableWidgetItem(edge_data.get('relationship', 'unknown'))
) )
self.connections.setItem(idx, 2, QTableWidgetItem("outgoing")) self.connections.setItem(idx, 2, QTableWidgetItem("outgoing"))
class GraphMLViewer3D(QMainWindow): class GraphMLViewer3D(QMainWindow):
"""Main window class for 3D GraphML visualization""" """Main window class for 3D GraphML visualization"""
def __init__(self): def __init__(self):
super().__init__() super().__init__()
@@ -186,7 +211,6 @@ class GraphMLViewer3D(QMainWindow):
self.elevation = 30 # Initial camera elevation self.elevation = 30 # Initial camera elevation
self.azimuth = 45 # Initial camera azimuth self.azimuth = 45 # Initial camera azimuth
self.init_ui() self.init_ui()
def init_ui(self): def init_ui(self):
@@ -233,9 +257,7 @@ class GraphMLViewer3D(QMainWindow):
# Set initial camera position # Set initial camera position
self.view.setCameraPosition( self.view.setCameraPosition(
distance=self.distance, distance=self.distance, elevation=self.elevation, azimuth=self.azimuth
elevation=self.elevation,
azimuth=self.azimuth
) )
# Connect all mouse events # Connect all mouse events
@@ -268,7 +290,6 @@ class GraphMLViewer3D(QMainWindow):
return sizes return sizes
def create_toolbar(self, layout: QVBoxLayout): def create_toolbar(self, layout: QVBoxLayout):
"""Create the toolbar with controls""" """Create the toolbar with controls"""
toolbar = QHBoxLayout() toolbar = QHBoxLayout()
@@ -310,8 +331,6 @@ class GraphMLViewer3D(QMainWindow):
reset_btn.clicked.connect(self.reset_view) # Use the new reset_view method reset_btn.clicked.connect(self.reset_view) # Use the new reset_view method
toolbar.addWidget(reset_btn) toolbar.addWidget(reset_btn)
def load_graphml(self) -> None: def load_graphml(self) -> None:
"""Load and visualize a GraphML file""" """Load and visualize a GraphML file"""
try: try:
@@ -338,11 +357,7 @@ class GraphMLViewer3D(QMainWindow):
if layout_type == "spring": if layout_type == "spring":
pos = nx.spring_layout( pos = nx.spring_layout(
self.graph, self.graph, dim=3, k=2.0, iterations=100, weight=None
dim=3,
k=2.0,
iterations=100,
weight=None
) )
elif layout_type == "circular": elif layout_type == "circular":
pos_2d = nx.circular_layout(self.graph) pos_2d = nx.circular_layout(self.graph)
@@ -365,13 +380,12 @@ class GraphMLViewer3D(QMainWindow):
def get_node_color(self, node_id: str) -> Tuple[float, float, float, float]: def get_node_color(self, node_id: str) -> Tuple[float, float, float, float]:
"""Get RGBA color based on community""" """Get RGBA color based on community"""
if hasattr(self, 'communities') and node_id in self.communities: if hasattr(self, "communities") and node_id in self.communities:
comm_id = self.communities[node_id] comm_id = self.communities[node_id]
color = self.community_colors[comm_id] color = self.community_colors[comm_id]
return tuple(color) return tuple(color)
return (0.5, 0.5, 0.5, 0.8) return (0.5, 0.5, 0.5, 0.8)
def create_node(self, node_id: str, position: np.ndarray, node_type: str) -> Node3D: def create_node(self, node_id: str, position: np.ndarray, node_type: str) -> Node3D:
"""Create a 3D node with interaction capabilities""" """Create a 3D node with interaction capabilities"""
color = self.get_node_color(node_id) color = self.get_node_color(node_id)
@@ -386,11 +400,11 @@ class GraphMLViewer3D(QMainWindow):
pos=np.array([position]), pos=np.array([position]),
size=np.array([size * 8]), size=np.array([size * 8]),
color=np.array([color]), color=np.array([color]),
pxMode=False pxMode=False,
) )
# Enable picking and set node ID # Enable picking and set node ID
node.mesh_item.setGLOptions('translucent') node.mesh_item.setGLOptions("translucent")
node.mesh_item.node_id = node_id node.mesh_item.node_id = node_id
if self.show_labels.isChecked(): if self.show_labels.isChecked():
@@ -402,9 +416,6 @@ class GraphMLViewer3D(QMainWindow):
return node return node
def mapToView(self, pos) -> Point: def mapToView(self, pos) -> Point:
"""Convert screen coordinates to world coordinates""" """Convert screen coordinates to world coordinates"""
# Get the viewport size # Get the viewport size
@@ -451,9 +462,7 @@ class GraphMLViewer3D(QMainWindow):
self.elevation = np.clip(self.elevation, -89, 89) self.elevation = np.clip(self.elevation, -89, 89)
self.view.setCameraPosition( self.view.setCameraPosition(
distance=self.distance, distance=self.distance, elevation=self.elevation, azimuth=self.azimuth
elevation=self.elevation,
azimuth=self.azimuth
) )
# Handle hover events when no buttons are pressed # Handle hover events when no buttons are pressed
@@ -462,14 +471,14 @@ class GraphMLViewer3D(QMainWindow):
mouse_pos = self.mapToView(pos) mouse_pos = self.mapToView(pos)
# Check for hover # Check for hover
min_dist = float('inf') min_dist = float("inf")
hovered_node = None hovered_node = None
for node_id, node in self.nodes.items(): for node_id, node in self.nodes.items():
# Calculate distance to mouse in world coordinates # Calculate distance to mouse in world coordinates
dx = mouse_pos.x - node.position[0] dx = mouse_pos.x - node.position[0]
dy = mouse_pos.y - node.position[1] dy = mouse_pos.y - node.position[1]
dist = np.sqrt(dx*dx + dy*dy) dist = np.sqrt(dx * dx + dy * dy)
if dist < min_dist and dist < 0.5: # Adjust threshold as needed if dist < min_dist and dist < 0.5: # Adjust threshold as needed
min_dist = dist min_dist = dist
@@ -496,13 +505,13 @@ class GraphMLViewer3D(QMainWindow):
mouse_pos = self.mapToView(pos) mouse_pos = self.mapToView(pos)
# Find closest node # Find closest node
min_dist = float('inf') min_dist = float("inf")
clicked_node = None clicked_node = None
for node_id, node in self.nodes.items(): for node_id, node in self.nodes.items():
dx = mouse_pos.x - node.position[0] dx = mouse_pos.x - node.position[0]
dy = mouse_pos.y - node.position[1] dy = mouse_pos.y - node.position[1]
dist = np.sqrt(dx*dx + dy*dy) dist = np.sqrt(dx * dx + dy * dy)
if dist < min_dist and dist < 0.5: # Adjust threshold as needed if dist < min_dist and dist < 0.5: # Adjust threshold as needed
min_dist = dist min_dist = dist
@@ -518,15 +527,14 @@ class GraphMLViewer3D(QMainWindow):
if self.graph: if self.graph:
self.details.update_node_info( self.details.update_node_info(
self.graph.nodes[clicked_node], self.graph.nodes[clicked_node], self.graph[clicked_node]
self.graph[clicked_node]
) )
def on_mouse_release(self, event): def on_mouse_release(self, event):
"""Handle mouse release events""" """Handle mouse release events"""
self.mouse_buttons_pressed.discard(event.button()) self.mouse_buttons_pressed.discard(event.button())
self.mouse_pos_last = None self.mouse_pos_last = None
def on_mouse_wheel(self, event): def on_mouse_wheel(self, event):
"""Handle mouse wheel for zooming""" """Handle mouse wheel for zooming"""
delta = event.angleDelta().y() delta = event.angleDelta().y()
@@ -539,9 +547,7 @@ class GraphMLViewer3D(QMainWindow):
self.distance = np.clip(self.distance, 1.0, 100.0) self.distance = np.clip(self.distance, 1.0, 100.0)
self.view.setCameraPosition( self.view.setCameraPosition(
distance=self.distance, distance=self.distance, elevation=self.elevation, azimuth=self.azimuth
elevation=self.elevation,
azimuth=self.azimuth
) )
def reset_view(self): def reset_view(self):
@@ -552,22 +558,22 @@ class GraphMLViewer3D(QMainWindow):
self.center = np.array([0, 0, 0]) self.center = np.array([0, 0, 0])
self.view.setCameraPosition( self.view.setCameraPosition(
distance=self.distance, distance=self.distance, elevation=self.elevation, azimuth=self.azimuth
elevation=self.elevation,
azimuth=self.azimuth
) )
def create_edge(
def create_edge(self, start_pos: np.ndarray, end_pos: np.ndarray, self,
color: Tuple[float, float, float, float] = (0.3, 0.3, 0.3, 0.2) start_pos: np.ndarray,
) -> gl.GLLinePlotItem: end_pos: np.ndarray,
color: Tuple[float, float, float, float] = (0.3, 0.3, 0.3, 0.2),
) -> gl.GLLinePlotItem:
"""Create a 3D edge between nodes""" """Create a 3D edge between nodes"""
return gl.GLLinePlotItem( return gl.GLLinePlotItem(
pos=np.array([start_pos, end_pos]), pos=np.array([start_pos, end_pos]),
color=color, color=color,
width=1, width=1,
antialias=True, antialias=True,
mode='lines' mode="lines",
) )
def handle_node_hover(self, event: Any, node_id: str) -> None: def handle_node_hover(self, event: Any, node_id: str) -> None:
@@ -595,8 +601,7 @@ class GraphMLViewer3D(QMainWindow):
if self.graph: if self.graph:
self.details.update_node_info( self.details.update_node_info(
self.graph.nodes[node_id], self.graph.nodes[node_id], self.graph[node_id]
self.graph[node_id]
) )
def refresh_layout(self) -> None: def refresh_layout(self) -> None:
@@ -620,7 +625,7 @@ class GraphMLViewer3D(QMainWindow):
positions = self.calculate_layout() positions = self.calculate_layout()
for node_id in self.graph.nodes(): for node_id in self.graph.nodes():
node_type = self.graph.nodes[node_id].get('type', 'default') node_type = self.graph.nodes[node_id].get("type", "default")
node = self.create_node(node_id, positions[node_id], node_type) node = self.create_node(node_id, positions[node_id], node_type)
self.view.addItem(node.mesh_item) self.view.addItem(node.mesh_item)
@@ -636,7 +641,7 @@ class GraphMLViewer3D(QMainWindow):
if self.show_labels.isChecked(): if self.show_labels.isChecked():
mid_point = (positions[source] + positions[target]) / 2 mid_point = (positions[source] + positions[target]) / 2
relationship = self.graph.edges[source, target].get('relationship', '') relationship = self.graph.edges[source, target].get("relationship", "")
if relationship: if relationship:
label = gl.GLTextItem( label = gl.GLTextItem(
pos=mid_point, pos=mid_point,
@@ -646,6 +651,7 @@ class GraphMLViewer3D(QMainWindow):
self.view.addItem(label) self.view.addItem(label)
self.edge_labels.append(label) self.edge_labels.append(label)
def main(): def main():
"""Application entry point""" """Application entry point"""
import sys import sys
@@ -655,5 +661,6 @@ def main():
viewer.show() viewer.show()
sys.exit(app.exec_()) sys.exit(app.exec_())
if __name__ == "__main__": if __name__ == "__main__":
main() main()