Atlantis is the Sardo Corso Graben Horst underwater continental block submerged by the Meltwater Pulses and destroyed by a subduction zone, Capital is Sulcis
# StrumentiTestualiUsai.py
# Progetto Integrato per l'Analisi Testuale e Narratologica
# Autore: Luigi Usai (con assistenza AI)
# Data: 19 Maggio 2025
# Versione: 2.1 (Aggiunto Menu Semantica e placeholder)
#
# Questo script Tkinter fornisce un'interfaccia grafica per caricare corpora testuali
# ed eseguire varie analisi linguistiche, di usabilità, narratologiche, semantiche (preliminari) e Griceane.
#
# Funzionalità incluse:
# - Caricamento di file di testo (.txt) come corpus.
# - Gestione (aggiunta/rimozione) di stopwords.
# - Analisi di frequenza dei termini.
# - Generazione di Nuvole di Parole.
# - Analisi di Collocazioni (N-grammi).
# - KWIC (Parole Chiave nel Contesto).
# - Andamento dei Termini attraverso i documenti o segmenti.
# - Rete di Co-occorrenze testuali.
# - Suddivisione in Frasi e Token (usabilità).
# - Annotazione Morfosintattica (POS Tagging - usabilità).
# - Calcolo Indice di Leggibilità Gulpease (globale e per frase - usabilità, specifico italiano).
# - Creazione e Visualizzazione Matrice Attanziale di Greimas (narratologia).
# - Creazione e Visualizzazione Matrice Funzioni di Propp (narratologia).
# - Creazione e Visualizzazione Tensori Narrativi (Luigi Usai - narratologia).
# - Generazione Permutazioni di Funzioni di Propp (trame possibili).
# - Generazione Combinazioni di Funzioni di Propp (sottoinsiemi di funzioni).
# - Visualizzazione Grafica Sequenza Funzioni di Propp (richiede Graphviz).
# - Analisi Semplificata Indicatori Griceani (Quantità, Modo, Qualità - richiede NLTK).
# - Funzioni Semantiche Preliminari (NER base, esplorazione WordNet).
# - Salvataggio Dati Narratologici (JSON, SQLite).
# - Finestra "About" con informazioni sull'autore.
#
# Dipendenze richieste:
# - tkinter (standard Python)
# - wordcloud (pip install wordcloud)
# - matplotlib (pip install matplotlib)
# - Pillow (PIL) (pip install Pillow) - Per l'immagine nell'About
# - nltk (pip install nltk) - Per tokenizzazione, POS tagging, Gulpease, Grice, Semantica base
# - graphviz (pip install graphviz) - Per visualizzazione sequenze Propp
# - itertools (standard Python)
# - json (standard Python)
# - sqlite3 (standard Python)
# - re (standard Python)
# - collections (standard Python)
# - statistics (standard Python)
# - math (standard Python)
# - textwrap (standard Python)
#
# Assicurati di scaricare i dati NLTK necessari
# Eseguendo in un interprete Python:
# import nltk
# nltk.download('punkt')
# nltk.download('averaged_perceptron_tagger')
# nltk.download('wordnet')
# nltk.download('omw-1.4') # Open Multilingual Wordnet, per supporto multilingua di WordNet
# nltk.download('maxent_ne_chunker') # Per Named Entity Recognition base
# nltk.download('words') # Necessario per maxent_ne_chunker
#
# Assicurati che il software Graphviz sia installato sul sistema e nel PATH per la visualizzazione grafica di Propp:
# https://graphviz.org/download/
#
# Per una panoramica sui Tensori Narrativi:
# https://www.amazon.it/ARCHITETTURE-INVISIBILI-VIAGGIO-NARRAZIONE-Versione/dp/B0F91P1XBH/
# Riferimento principale delle opere: Harvard Dataverse, DOI:10.7910/DVN/ICOJ19
#
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext, simpledialog
import json
import sqlite3
import itertools
import re
from collections import Counter
import statistics
import math
import textwrap # Per gestire il testo lungo nei nodi graphviz
# --- Gestione Import Opzionali e Dipendenze ---
# Controlla la disponibilità delle librerie non standard e dei dati NLTK
# Pillow (PIL) per immagine About
pil_disponibile = False
try:
from PIL import Image, ImageTk
pil_disponibile = True
except ImportError:
print("Pillow (PIL) non è installato. L'immagine nell'About non sarà visualizzata. Installa con: pip install Pillow")
# NLTK per analisi linguistiche, usabilità, Grice e Semantica
nltk_disponibile = False
nltk_punkt_disponibile = False
nltk_tagger_disponibile = False
nltk_wordnet_disponibile = False
nltk_ne_chunker_disponibile = False
nltk_words_disponibile = False
try:
import nltk
nltk_disponibile = True
try:
from nltk.corpus import wordnet # Tentativo di importare wordnet per verificarne la disponibilità
except ImportError:
print("Modulo nltk.corpus.wordnet non trovato. Alcune funzionalità semantiche potrebbero non funzionare.")
# Verifica la presenza dei dati NLTK necessari
def check_nltk_data(resource_name, resource_path):
try:
nltk.data.find(resource_path)
return True
except (nltk.downloader.DownloadError, LookupError):
print(f"Pacchetto NLTK '{resource_name}' ({resource_path}) non trovato.")
print(f"Scaricalo eseguendo in Python: nltk.download('{resource_name}')")
return False
nltk_punkt_disponibile = check_nltk_data('punkt', 'tokenizers/punkt')
nltk_tagger_disponibile = check_nltk_data('averaged_perceptron_tagger', 'taggers/averaged_perceptron_tagger')
nltk_wordnet_disponibile = check_nltk_data('wordnet', 'corpora/wordnet')
if nltk_wordnet_disponibile: # omw-1.4 dipende da wordnet
nltk_wordnet_disponibile = check_nltk_data('omw-1.4', 'corpora/omw-1.4') # Open Multilingual Wordnet
nltk_ne_chunker_disponibile = check_nltk_data('maxent_ne_chunker', 'chunkers/maxent_ne_chunker/english_ace_multiclass.pickle') # Path specifico
nltk_words_disponibile = check_nltk_data('words', 'corpora/words')
except ImportError:
print("Libreria NLTK non trovata. Molte funzionalità non saranno disponibili. Installa con: pip install nltk")
# Graphviz per visualizzazione Propp
graphviz_disponibile = False
try:
import graphviz
graphviz_disponibile = True
except ImportError:
print("Libreria 'graphviz' non trovata. La visualizzazione delle sequenze di Propp non sarà disponibile.")
print("Installala con: pip install graphviz")
print("Inoltre, assicurati che il software Graphviz sia installato sul sistema e nel PATH: https://graphviz.org/download/")
# WordCloud e Matplotlib per nuvola di parole e andamento termini
wordcloud_disponibile = False
matplotlib_disponibile = False
try:
from wordcloud import WordCloud
import matplotlib.pyplot as plt
wordcloud_disponibile = True
matplotlib_disponibile = True
except ImportError:
print("Librerie 'wordcloud' o 'matplotlib' non trovate. La nuvola di parole e l'andamento termini non saranno disponibili.")
print("Installale con: pip install wordcloud matplotlib")
# --- Costanti e Definizioni ---
# Definizioni delle 31 funzioni di Propp
FUNZIONI_PROPP = {
"F1": "Allontanamento (Un membro della famiglia si allontana)",
"F2": "Divieto (All'eroe è imposto un divieto)",
"F3": "Infrazione (Il divieto è infranto)",
"F4": "Investigazione (L'antagonista tenta una ricognizione)",
"F5": "Delazione (L'antagonista riceve informazioni sulla vittima)",
"F6": "Tranello (L'antagonista tenta di ingannare la vittima)",
"F7": "Connivenza (La vittima cade nell'inganno)",
"F8": "Danneggiamento/Mancanza (L'antagonista danneggia o causa una mancanza)",
"F9": "Mediazione (Il danneggiamento/mancanza è reso noto, l'eroe è sollecitato)",
"F10": "Consenso dell'Eroe (L'eroe accetta di reagire)",
"F11": "Partenza dell'Eroe (L'eroe lascia la casa)",
"F12": "Messa alla Prova (L'eroe è messo alla prova dal donatore)",
"F13": "Reazione dell'Eroe (L'eroe reagisce alle azioni del donatore)",
"F14": "Conseguimento Mezzo Magico (L'eroe ottiene un mezzo magico)",
"F15": "Trasferimento (L'eroe è trasferito vicino all'oggetto della ricerca)",
"F16": "Lotta (L'eroe e l'antagonista si scontrano)",
"F17": "Marchiatura (All'eroe è impresso un marchio)",
"F18": "Vittoria (L'antagonista è sconfitto)",
"F19": "Rimozione Danno/Mancanza (Il danno/mancanza iniziale è rimosso)",
"F20": "Ritorno dell'Eroe (L'eroe ritorna)",
"F21": "Persecuzione (L'eroe è perseguitato)",
"F22": "Salvataggio (L'eroe è salvato dalla persecuzione)",
"F23": "Arrivo in Incognito (L'eroe arriva non riconosciuto)",
"F24": "Pretese Falso Eroe (Un falso eroe avanza pretese)",
"F25": "Compito Difficile (All'eroe è proposto un compito difficile)",
"F26": "Adempimento Compito (Il compito è portato a termine)",
"F27": "Riconoscimento (L'eroe è riconosciuto)",
"F28": "Smascheramento (Il falso eroe o l'antagonista è smascherato)",
"F29": "Trasfigurazione (All'eroe è data una nuova apparenza)",
"F30": "Punizione (L'antagonista è punito)",
"F31": "Nozze/Ricompensa (L'eroe si sposa o è ricompensato)"
}
# Lista (molto limitata) di possibili indicatori di "hedging" (copertura/incertezza) per Grice
HEDGING_TERMS = ["credo", "penso", "forse", "magari", "sembra", "parrebbe", "apparentemente", "in un certo senso", "tipo", "cioè", "insomma"]
# --- Classi per Funzionalità Specifiche ---
class FunzioniUsability:
"""Contiene funzioni per l'analisi di usabilità e leggibilità del testo."""
def __init__(self, app_ref):
self.app_ref = app_ref
self.lingua_analisi = "italian" # Default per funzioni come Gulpease
def imposta_lingua_analisi(self):
"""Permette all'utente di impostare la lingua per le analisi che la supportano."""
lingua_scelta = simpledialog.askstring("Imposta Lingua Analisi",
"Scegli la lingua per l'analisi (es. 'italian', 'english').\n"
"Nota: Gulpease funziona solo per l'italiano.\n"
"WordNet supporta più lingue se 'omw-1.4' è scaricato.",
initialvalue=self.lingua_analisi,
parent=self.app_ref.root)
if lingua_scelta:
lingua_scelta_lower = lingua_scelta.strip().lower()
# Aggiungere altre lingue supportate da NLTK se necessario
# nltk.corpus.omw.langs() mostra le lingue supportate da OMW
supported_langs_nltk = ['english', 'italian', 'french', 'spanish', 'german'] # Esempio
if lingua_scelta_lower in supported_langs_nltk: # Ampliare con nltk.corpus.omw.langs() se necessario
self.lingua_analisi = lingua_scelta_lower
messagebox.showinfo("Lingua Impostata", f"Lingua per l'analisi impostata a: {self.lingua_analisi}", parent=self.app_ref.root)
self.app_ref._display_output("Impostazione Lingua", f"Lingua analisi: {self.lingua_analisi}")
else:
messagebox.showwarning("Lingua non Supportata", f"Lingua '{lingua_scelta}' non pienamente supportata o riconosciuta per tutte le funzioni.\nMantengo: {self.lingua_analisi}", parent=self.app_ref.root)
def _check_corpus_e_nltk(self, check_punkt=False, check_tagger=False, check_wordnet=False, check_ne_chunker=False, check_words=False):
"""Controlla se il corpus è caricato e se NLTK e i suoi componenti sono disponibili."""
if not self.app_ref.corpus_testuale:
messagebox.showwarning("Corpus Vuoto", "Per favore, carica prima un corpus testuale.", parent=self.app_ref.root)
return False
if not nltk_disponibile:
messagebox.showerror("NLTK Mancante", "La libreria NLTK è necessaria per questa funzionalità.", parent=self.app_ref.root)
return False
if check_punkt and not nltk_punkt_disponibile:
messagebox.showerror("Dipendenza NLTK Mancante", "Il pacchetto 'punkt' di NLTK è necessario.\nScaricalo con: nltk.download('punkt')", parent=self.app_ref.root)
return False
if check_tagger and not nltk_tagger_disponibile:
messagebox.showerror("Dipendenza NLTK Mancante", "Il pacchetto 'averaged_perceptron_tagger' di NLTK è necessario.\nScaricalo con: nltk.download('averaged_perceptron_tagger')", parent=self.app_ref.root)
return False
if check_wordnet and not nltk_wordnet_disponibile:
messagebox.showerror("Dipendenza NLTK Mancante", "I pacchetti 'wordnet' e 'omw-1.4' di NLTK sono necessari.\nScaricali con: nltk.download('wordnet') e nltk.download('omw-1.4')", parent=self.app_ref.root)
return False
if check_ne_chunker and not nltk_ne_chunker_disponibile:
messagebox.showerror("Dipendenza NLTK Mancante", "Il pacchetto 'maxent_ne_chunker' di NLTK è necessario.\nScaricalo con: nltk.download('maxent_ne_chunker')", parent=self.app_ref.root)
return False
if check_words and not nltk_words_disponibile: # 'words' è richiesto da 'maxent_ne_chunker'
messagebox.showerror("Dipendenza NLTK Mancante", "Il pacchetto 'words' di NLTK è necessario (usato da NER).\nScaricalo con: nltk.download('words')", parent=self.app_ref.root)
return False
return True
def subdividi_in_frasi(self):
"""Suddivide il corpus in frasi e le visualizza."""
if not self._check_corpus_e_nltk(check_punkt=True):
return
testo_completo = ' '.join(self.app_ref.corpus_testuale)
try:
frasi = nltk.sent_tokenize(testo_completo, language=self.lingua_analisi)
output_str = f"Suddivisione in Frasi (Lingua: {self.lingua_analisi}):\n"
output_str += "-------------------------------------------------\n"
if not frasi:
output_str += "Nessuna frase trovata."
else:
max_frasi_visualizzate = 500
for i, frase in enumerate(frasi):
if i >= max_frasi_visualizzate:
output_str += f"\n... e altre {len(frasi) - max_frasi_visualizzate} frasi non visualizzate."
break
output_str += f"Frase {i+1}: {frase}\n"
self.app_ref._display_output("Suddivisione in Frasi", output_str)
except Exception as e:
messagebox.showerror("Errore Suddivisione Frasi", f"Errore: {e}", parent=self.app_ref.root)
self.app_ref._display_output("Errore Suddivisione Frasi", f"Errore: {e}")
def subdividi_in_token(self):
"""Suddivide il corpus in token e li visualizza."""
if not self._check_corpus_e_nltk(check_punkt=True):
return
testo_completo = ' '.join(self.app_ref.corpus_testuale)
try:
tokens = nltk.word_tokenize(testo_completo, language=self.lingua_analisi)
output_str = f"Suddivisione in Token (Lingua: {self.lingua_analisi}):\n"
output_str += "--------------------------------------------------\n"
if not tokens:
output_str += "Nessun token trovato."
else:
max_tokens_visualizzati = 1000
display_tokens = tokens[:max_tokens_visualizzati]
output_str += ", ".join(display_tokens)
if len(tokens) > max_tokens_visualizzati:
output_str += f"\n... e altri {len(tokens) - max_tokens_visualizzati} token non visualizzati."
self.app_ref._display_output("Suddivisione in Token", output_str)
except Exception as e:
messagebox.showerror("Errore Suddivisione Token", f"Errore: {e}", parent=self.app_ref.root)
self.app_ref._display_output("Errore Suddivisione Token", f"Errore: {e}")
def annotazione_pos(self):
"""Esegue il Part-of-Speech tagging sul corpus e visualizza i risultati."""
if not self._check_corpus_e_nltk(check_punkt=True, check_tagger=True):
return
testo_completo = ' '.join(self.app_ref.corpus_testuale)
try:
tokens = nltk.word_tokenize(testo_completo, language=self.lingua_analisi)
# Per l'italiano, è meglio specificare lang='ita' se si usa un tagger addestrato per l'italiano.
# NLTK di default usa 'averaged_perceptron_tagger' addestrato su Penn Treebank (inglese).
# Per risultati migliori in italiano, si dovrebbe usare un modello specifico per l'italiano.
# Qui usiamo il tagger di default, avvisando l'utente.
# Se la lingua è 'italian', NLTK potrebbe tentare di usare un tagger specifico se disponibile,
# ma 'averaged_perceptron_tagger' non ha un parametro 'language' diretto.
# nltk.pos_tag(tokens, lang=self.lingua_analisi) potrebbe funzionare con alcuni tagger.
tagged_tokens = nltk.pos_tag(tokens) # Default tagger
output_str = f"Annotazione Morfosintattica (POS Tagging - Lingua: {self.lingua_analisi}):\n"
output_str += "-------------------------------------------------------------------\n"
if not tagged_tokens:
output_str += "Nessun token da annotare."
else:
max_tagged_visualizzati = 500
for i, (token, tag) in enumerate(tagged_tokens):
if i >= max_tagged_visualizzati:
output_str += f"\n... e altri {len(tagged_tokens) - max_tagged_visualizzati} token non visualizzati."
break
output_str += f"{token} [{tag}]\n"
output_str += "\nNota: Il tagger predefinito di NLTK ('averaged_perceptron_tagger') è ottimizzato per l'inglese."
if self.lingua_analisi == "italian":
output_str += "\nPer l'italiano, i risultati potrebbero non essere ottimali senza un modello specifico addestrato."
self.app_ref._display_output("Annotazione POS", output_str)
except Exception as e:
messagebox.showerror("Errore Annotazione POS", f"Errore: {e}", parent=self.app_ref.root)
self.app_ref._display_output("Errore Annotazione POS", f"Errore: {e}")
def calcola_gulpease_globale(self):
"""
Calcola l'indice di leggibilità Gulpease per l'intero corpus.
Questa funzione è specifica per la lingua ITALIANA.
"""
if self.lingua_analisi != "italian":
messagebox.showwarning("Lingua non Adatta", "L'indice Gulpease è calibrato per la lingua italiana. "
f"La lingua attualmente impostata è '{self.lingua_analisi}'. "
"Il risultato potrebbe non essere attendibile.", parent=self.app_ref.root)
if not self._check_corpus_e_nltk(check_punkt=True):
return
testo_completo = ' '.join(self.app_ref.corpus_testuale)
try:
parole_raw = nltk.word_tokenize(testo_completo.lower(), language='italian')
parole = [p for p in parole_raw if p.isalpha()]
num_parole = len(parole)
if num_parole == 0:
self.app_ref._display_output("Indice Gulpease", "Nessuna parola alfabetica valida trovata per il calcolo.")
messagebox.showwarning("Indice Gulpease", "Nessuna parola valida trovata per il calcolo.", parent=self.app_ref.root)
return
frasi = nltk.sent_tokenize(testo_completo, language='italian')
num_frasi = len(frasi)
if num_frasi == 0:
self.app_ref._display_output("Indice Gulpease", "Nessuna frase trovata per il calcolo.")
messagebox.showwarning("Indice Gulpease", "Nessuna frase trovata per il calcolo.", parent=self.app_ref.root)
return
num_lettere = sum(len(p) for p in parole)
if num_parole > 0:
gulpease_index = 89 + ( (num_frasi * 300) - (num_lettere * 10) ) / num_parole
else:
gulpease_index = 0
gulpease_index = max(0, min(100, gulpease_index))
interpretazione = ""
if gulpease_index >= 80: interpretazione = "Molto facile (lettori con licenza elementare)"
elif gulpease_index >= 60: interpretazione = "Facile (lettori con licenza media inferiore)"
elif gulpease_index >= 40: interpretazione = "Abbastanza difficile (lettori con licenza media superiore)"
else: interpretazione = "Difficile (lettori con laurea)"
output_str = f"Indice di Leggibilità Globale Gulpease (per l'italiano):\n"
output_str += "-------------------------------------------------------\n"
output_str += f"Numero di Lettere (alfabetiche): {num_lettere}\n"
output_str += f"Numero di Parole (alfabetiche): {num_parole}\n"
output_str += f"Numero di Frasi: {num_frasi}\n"
output_str += f"Indice Gulpease: {gulpease_index:.2f}\n"
output_str += f"Interpretazione: {interpretazione}\n\n"
output_str += "Scala di riferimento Gulpease:\n"
output_str += " > 80: Molto facile\n 60-80: Facile\n 40-60: Abbastanza difficile\n < 40: Difficile\n"
if self.lingua_analisi != "italian":
output_str += f"\nATTENZIONE: Calcolato usando metriche italiane su testo potenzialmente non italiano ('{self.lingua_analisi}')."
self.app_ref._display_output("Indice Gulpease Globale", output_str)
except Exception as e:
messagebox.showerror("Errore Gulpease", f"Errore durante il calcolo dell'indice Gulpease: {e}", parent=self.app_ref.root)
self.app_ref._display_output("Errore Gulpease", f"Errore: {e}")
def analisi_leggibilita_per_frase(self):
"""
Calcola l'indice Gulpease per ogni frase del testo.
Specifico per ITALIANO.
"""
if self.lingua_analisi != "italian":
messagebox.showwarning("Lingua non Adatta", "L'indice Gulpease è calibrato per la lingua italiana. "
f"La lingua attualmente impostata è '{self.lingua_analisi}'. "
"I risultati per frase potrebbero non essere attendibili.", parent=self.app_ref.root)
if not self._check_corpus_e_nltk(check_punkt=True):
return
testo_completo = ' '.join(self.app_ref.corpus_testuale)
try:
frasi_originali = nltk.sent_tokenize(testo_completo, language='italian')
if not frasi_originali:
self.app_ref._display_output("Leggibilità per Frase", "Nessuna frase trovata.")
messagebox.showwarning("Leggibilità per Frase", "Nessuna frase trovata.", parent=self.app_ref.root)
return
output_str = f"Analisi Leggibilità per Frase (Indice Gulpease - per l'italiano):\n"
output_str += "-----------------------------------------------------------------\n"
risultati_frasi = []
max_frasi_visualizzate = 300
for i, frase_txt in enumerate(frasi_originali):
if i >= max_frasi_visualizzate:
risultati_frasi.append(f"\n--- (Visualizzazione limitata alle prime {max_frasi_visualizzate} frasi) ---")
break
parole_raw_frase = nltk.word_tokenize(frase_txt.lower(), language='italian')
parole_frase = [p for p in parole_raw_frase if p.isalpha()]
num_parole_frase = len(parole_frase)
if num_parole_frase == 0:
risultati_frasi.append(f"Frase {i+1}: \"{frase_txt[:70]}...\" - Indice Gulpease: N/A (0 parole)")
continue
num_lettere_frase = sum(len(p) for p in parole_frase)
if num_parole_frase > 0:
gulpease_frase = 89 + ( (1 * 300) - (num_lettere_frase * 10) ) / num_parole_frase
else:
gulpease_frase = 0
gulpease_frase = max(0, min(100, gulpease_frase))
interpretazione_frase = ""
if gulpease_frase >= 80: interpretazione_frase = "Molto facile"
elif gulpease_frase >= 60: interpretazione_frase = "Facile"
elif gulpease_frase >= 40: interpretazione_frase = "Abb. difficile"
else: interpretazione_frase = "Difficile"
risultati_frasi.append(f"Frase {i+1}: \"{frase_txt[:70]}...\"\n Indice Gulpease: {gulpease_frase:.2f} ({interpretazione_frase}) "
f"[L:{num_lettere_frase}, P:{num_parole_frase}]")
output_str += "\n\n".join(risultati_frasi)
if self.lingua_analisi != "italian":
output_str += f"\n\nATTENZIONE: Calcolato usando metriche italiane su testo potenzialmente non italiano ('{self.lingua_analisi}')."
self.app_ref._display_output("Leggibilità per Frase (Gulpease)", output_str)
except Exception as e:
messagebox.showerror("Errore Leggibilità per Frase", f"Errore: {e}", parent=self.app_ref.root)
self.app_ref._display_output("Errore Leggibilità per Frase", f"Errore: {e}")
class FunzioniNarratologia:
"""Contiene funzioni per l'analisi e la generazione basata su modelli narratologici (Greimas, Propp, Tensori)."""
def __init__(self, app_ref):
self.app_ref = app_ref
self.matrice_greimas_data = None
self.matrice_propp_data_utente = None
self.tensori_narrativi_data = None
def get_propp_function_description(self, code):
funzioni_di_riferimento = self.matrice_propp_data_utente if self.matrice_propp_data_utente is not None else FUNZIONI_PROPP
return funzioni_di_riferimento.get(code.upper(), f"Funzione Sconosciuta ({code})")
def crea_matrice_greimas(self):
messagebox.showinfo("Matrice Attanziale di Greimas",
"Definisci i ruoli attanziali (Soggetto, Oggetto, Destinante, Destinatario, Aiutante, Oppositore) per la tua analisi.",
parent=self.app_ref.root)
dialog = tk.Toplevel(self.app_ref.root)
dialog.title("Matrice Attanziale di Greimas")
dialog.geometry("400x350")
dialog.transient(self.app_ref.root)
dialog.grab_set()
tk.Label(dialog, text="Definisci gli attanti:", font=("Arial", 12, "bold")).pack(pady=10)
attanti = ["Soggetto", "Oggetto", "Destinante", "Destinatario", "Aiutante", "Oppositore"]
entries = {}
# Mantieni un riferimento alle entry per pre-popolare
self.greimas_entries = {}
for attante in attanti:
frame = tk.Frame(dialog)
frame.pack(fill=tk.X, padx=20, pady=5)
tk.Label(frame, text=f"{attante}:", width=15, anchor="w").pack(side=tk.LEFT)
entry = tk.Entry(frame, width=30)
entry.pack(side=tk.RIGHT, expand=True, fill=tk.X)
self.greimas_entries[attante] = entry # Salva riferimento
if self.matrice_greimas_data and attante in self.matrice_greimas_data:
entry.insert(0, self.matrice_greimas_data[attante])
entries[attante] = entry
def salva_dati_greimas():
self.matrice_greimas_data = {attante: entries[attante].get().strip() for attante in attanti}
output_str = "Matrice Attanziale di Greimas:\n"
output_str += "-----------------------------\n"
for attante, valore in self.matrice_greimas_data.items():
output_str += f" - {attante}: {valore if valore else '[Non definito]'}\n"
self.app_ref._display_output("Matrice Attanziale (Greimas)", output_str)
messagebox.showinfo("Matrice Greimas", "Matrice di Greimas aggiornata e visualizzata.", parent=dialog)
dialog.destroy()
tk.Button(dialog, text="Salva e Visualizza", command=salva_dati_greimas).pack(pady=20)
tk.Button(dialog, text="Annulla", command=dialog.destroy).pack(pady=5)
self.app_ref.root.wait_window(dialog)
def crea_matrice_propp(self):
messagebox.showinfo("Matrice Funzioni di Propp",
"Qui puoi definire un sottoinsieme o personalizzare le descrizioni delle Funzioni di Propp.\n"
"Inserisci le funzioni nel formato 'Codice: Descrizione' (una per riga).\n"
"Se vuoi usare le 31 funzioni standard, puoi saltare questo passaggio o cliccare 'Usa Standard'.",
parent=self.app_ref.root)
dialog = tk.Toplevel(self.app_ref.root)
dialog.title("Definisci Funzioni di Propp Personalizzate")
dialog.geometry("500x600")
dialog.transient(self.app_ref.root)
dialog.grab_set()
tk.Label(dialog, text="Definisci/Modifica Funzioni di Propp:", font=("Arial", 12, "bold")).pack(pady=10)
tk.Label(dialog, text="Formato: Codice (es. F1): Descrizione", font=("Arial", 10, "italic")).pack()
text_area = scrolledtext.ScrolledText(dialog, wrap=tk.WORD, width=50, height=15, font=("Arial", 10))
text_area.pack(padx=10, pady=5, fill=tk.BOTH, expand=True)
if self.matrice_propp_data_utente:
for codice, desc in self.matrice_propp_data_utente.items():
text_area.insert(tk.END, f"{codice}: {desc}\n")
else:
for codice, desc in FUNZIONI_PROPP.items():
text_area.insert(tk.END, f"{codice}: {desc}\n")
def salva_dati_propp():
input_text = text_area.get(1.0, tk.END).strip()
lines = input_text.split('\n')
new_propp_data = {}
errors = []
for line in lines:
line = line.strip()
if not line or line.startswith('#'): continue
if ':' in line:
codice, desc = line.split(':', 1)
codice = codice.strip().upper()
desc = desc.strip()
if codice and desc:
if re.match(r'^F\d+', contesto_sx_list[k_idx+1]):
contesto_sx_str += " "
elif k_idx == len(contesto_sx_list) -1 and not re.match(r'^[\.,;!?\'"]', k_tok) and not re.match(r'^[\.,;!?\'"]', k_tok) and not re.match(r'^[\.,;!?\'"]$', contesto_dx_list[k_idx-1]):
contesto_dx_str += " "
contesto_dx_str += k_tok
results_kwic.append(f"...{contesto_sx_str}[{parola_target}]{contesto_dx_str}...")
if results_kwic:
output_str = f"KWIC per '{parola_chiave}' (contesto: {contesto_size} token, {found_count} occorrenze trovate):\n\n" + "\n".join(results_kwic)
else:
output_str = f"Nessuna occorrenza trovata per '{parola_chiave}' nel corpus."
self._display_output(f"KWIC: {parola_chiave}", output_str)
def andamento(self):
if not matplotlib_disponibile:
messagebox.showerror("Libreria Mancante", "La libreria 'matplotlib' è necessaria per questa funzionalità.", parent=self.root)
return
if not self.corpus_testuale:
messagebox.showwarning("Corpus Vuoto", "Per favore, carica prima un corpus testuale.", parent=self.root)
return
parola_chiave = simpledialog.askstring("Andamento Termine", "Inserisci la parola chiave per l'analisi dell'andamento:", parent=self.root)
if not parola_chiave: return
parola_chiave_lower = parola_chiave.strip().lower()
frequencies = []
segment_labels = []
if len(self.corpus_testuale) == 1:
num_chunks = simpledialog.askinteger("Numero Segmenti", "Dividi il documento in quanti segmenti per l'analisi?",
parent=self.root, minvalue=2, maxvalue=100, initialvalue=10)
if num_chunks is None: return
words_in_doc = re.findall(r'\b\w+\b', self.corpus_testuale[0].lower())
if not words_in_doc:
self._display_output("Andamento Termine", "Il documento selezionato è vuoto o non contiene parole.")
messagebox.showwarning("Andamento Termine", "Il documento selezionato è vuoto o non contiene parole.", parent=self.root)
return
if len(words_in_doc) < num_chunks :
messagebox.showwarning("Segmenti Eccessivi", f"Il documento contiene solo {len(words_in_doc)} parole. Non può essere diviso in {num_chunks} segmenti. Verrà usato un segmento per parola (max {len(words_in_doc)} segmenti).", parent=self.root)
num_chunks = len(words_in_doc)
if num_chunks < 1:
self._display_output("Andamento Termine", "Il documento non ha parole per creare segmenti.")
return
chunk_size = max(1, len(words_in_doc) // num_chunks)
for i in range(num_chunks):
start_idx = i * chunk_size
end_idx = (i + 1) * chunk_size if i < num_chunks - 1 else len(words_in_doc)
chunk = words_in_doc[start_idx:end_idx]
if chunk:
frequencies.append(chunk.count(parola_chiave_lower))
segment_labels.append(f"Seg. {i+1}")
if not frequencies:
self._display_output("Andamento Termine", f"La parola '{parola_chiave}' non è stata trovata o i segmenti erano vuoti.")
messagebox.showinfo("Andamento Termine", f"La parola '{parola_chiave}' non è stata trovata o i segmenti erano vuoti.", parent=self.root)
return
plot_title = f"Andamento di '{parola_chiave}' (Doc. '{self.nomi_file_corpus[0]}' in {num_chunks} segmenti)"
plot_xlabel = "Segmento del Testo"
plot_type = 'line'
else:
for i, testo_doc in enumerate(self.corpus_testuale):
parole_doc = re.findall(r'\b\w+\b', testo_doc.lower())
frequencies.append(parole_doc.count(parola_chiave_lower))
segment_labels.append(self.nomi_file_corpus[i] if self.nomi_file_corpus and i < len(self.nomi_file_corpus) else f"Doc {i+1}")
plot_title = f"Andamento di '{parola_chiave}' attraverso i Documenti Caricati"
plot_xlabel = "Documento"
plot_type = 'bar'
if not frequencies or all(f == 0 for f in frequencies):
self._display_output("Andamento Termine", f"Nessuna occorrenza di '{parola_chiave}' trovata nel corpus per il grafico.")
messagebox.showinfo("Andamento Termine", f"La parola '{parola_chiave}' non è stata trovata nel corpus.", parent=self.root)
return
plt.figure(figsize=(12, 7))
if plot_type == 'line':
plt.plot(segment_labels, frequencies, marker='o', linestyle='-', color='dodgerblue')
else:
plt.bar(segment_labels, frequencies, color='skyblue')
plt.title(plot_title, fontsize=14)
plt.xlabel(plot_xlabel, fontsize=12)
plt.ylabel("Frequenza Assoluta", fontsize=12)
if len(segment_labels) > 10:
plt.xticks(rotation=45, ha="right", fontsize=10)
else:
plt.xticks(fontsize=10)
plt.yticks(fontsize=10)
plt.grid(axis='y', linestyle='--')
plt.tight_layout()
plt.show()
self._display_output("Andamento Termine", f"Grafico dell'andamento di '{parola_chiave}' generato e visualizzato.")
def vista_rete(self):
if not self.corpus_testuale:
messagebox.showwarning("Corpus Vuoto", "Per favore, carica prima un corpus testuale.", parent=self.root)
return
window_size = simpledialog.askinteger("Finestra di Contesto (Co-occorrenze)",
"Inserisci la dimensione della finestra di contesto (numero di parole vicine da considerare):",
parent=self.root, minvalue=2, maxvalue=10, initialvalue=3)
if window_size is None: return
num_cooc = simpledialog.askinteger("Numero Co-occorrenze",
"Quante coppie di co-occorrenze più frequenti vuoi visualizzare?",
parent=self.root, minvalue=1, initialvalue=15)
if num_cooc is None: return
parole = self._get_processed_words(remove_stopwords=True)
if len(parole) < window_size:
self._display_output("Rete Co-occorrenze", f"Non ci sono abbastanza parole nel corpus (dopo rimozione stopwords) per analizzare le co-occorrenze con una finestra di dimensione {window_size}.")
messagebox.showinfo("Rete Co-occorrenze", "Testo insufficiente per l'analisi delle co-occorrenze.", parent=self.root)
return
co_occurrences = Counter()
for i in range(len(parole) - window_size + 1):
window_segment = parole[i : i + window_size]
parole_nella_finestra_uniche = sorted(list(set(window_segment)))
if len(parole_nella_finestra_uniche) < 2: continue
for j in range(len(parole_nella_finestra_uniche)):
for k in range(j + 1, len(parole_nella_finestra_uniche)):
pair = tuple(sorted((parole_nella_finestra_uniche[j], parole_nella_finestra_uniche[k])))
co_occurrences[pair] += 1
if not co_occurrences:
self._display_output("Rete Co-occorrenze", "Nessuna co-occorrenza trovata con i parametri specificati.")
messagebox.showinfo("Rete Co-occorrenze", "Nessuna co-occorrenza trovata.", parent=self.root)
return
output_str = f"Le {num_cooc} coppie di termini co-occorrenti più frequenti (finestra: {window_size} parole, stopwords escluse):\n"
output_str += "--------------------------------------------------------------------------------------\n"
for (p1, p2), freq in co_occurrences.most_common(num_cooc):
output_str += f"('{p1}', '{p2}'): {freq}\n"
output_str += "\nNota: Questa è una rappresentazione testuale. Per una visualizzazione grafica della rete, sarebbero necessarie librerie aggiuntive (es. NetworkX, Matplotlib)."
self._display_output("Rete di Co-occorrenze", output_str)
messagebox.showinfo("Rete di Co-occorrenze", "Analisi delle co-occorrenze completata. I risultati sono nell'area di output.", parent=self.root)
# --- Blocco Principale per l'Esecuzione dell'Applicazione ---
if __name__ == '__main__':
radice = tk.Tk()
app = StrumentiTestualiUsai(radice)
radice.mainloop()
We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept All”, you consent to the use of ALL the cookies. However, you may visit "Cookie Settings" to provide a controlled consent.
This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.
Cookie
Durata
Descrizione
cookielawinfo-checkbox-analytics
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics".
cookielawinfo-checkbox-functional
11 months
The cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".
cookielawinfo-checkbox-necessary
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary".
cookielawinfo-checkbox-others
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other.
cookielawinfo-checkbox-performance
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance".
viewed_cookie_policy
11 months
The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.
Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.