## page was renamed from mtramonti2007/provaGUIPython3 ## page was renamed from Programmazione/Python/GUI ## page was renamed from LinguaggioPython/GUI #format wiki #language it <
> <> <> = Introduzione = Questa pagina presenta alcuni esempi su come creare con Python programmi dotati di una semplice interfaccia grafica usando uno dei moduli '''GTK''', '''!PyQt''', '''tkinter''' o '''wxPython''' che si appoggiano tutti a librerie Open Source e multipiattaforma.<
> In ciascuno di questi esempi viene realizzata una versione grafica di ''!AreaTriangolo_con_eccezioni.py'', script presente all'interno di [[Programmazione/Python/ListatiPython3#Listato_3| questa pagina]]<
> Poiché tutti i moduli citati e le loro librerie usano la programmazione orientata agli oggetti, è possibile riutilizzare in maniera modulare lo stesso codice in più applicazioni riducendo tempo e risorse per lo sviluppo. ## * questi esempi sono scritti per l'interprete '''Python 3.x''' per la guida nella precedente versione '''Python 2.7.xx''', vedi [[Programmazione/Python/InterfacceGrafiche| InterfacceGrafiche]] = GTK = '''GTK+''' è un toolkit per la creazione di interfacce grafiche che è stato progettato inizialmente come ausilio alla programmazione per [[Grafica/Gimp|GIMP]] ed è diventato una parte fondamentale dell'ambiente [[AmbienteGrafico/Gnome| GNOME]].<
> Il programma è preinstallato in '''Ubuntu'''. Nel caso fosse stato inavvertitamente rimosso o mancasse [[AmministrazioneSistema/InstallareProgrammi|installare]] il pacchetto [[apt://python3-gi|python3-gi]]. ##Il pacchetto fornisce il modulo '''gi''' il quale provvede ai collegamenti '''Python3''' alle librerie '''GTK+''' versione 3 e seguenti. ##'''PyGTK''' è un set di moduli che permettono l'interfacciamento tra '''Python''' e le librerie '''GTK+''', è un toolkit orientato agli oggetti, permette quindi la possibilità di riutilizzare dello stesso codice in più applicazioni. === Esempio di applicazione === {{{#!python #!/usr/bin/python3 import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk # Importa il modulo per l'interfaccia grafica from math import sqrt # Importa la funzione sqrt (radice quadrata) dal modulo math class TriangoloImpossibile(Exception): """Classe per sollevare un'eccezione se vengono immesse misure incompatibili.""" def __init__(self, message="Un triangolo con questi lati non esiste."): self.message = message class Triangolo: """Classe per costruire e validare gli oggetti triangolo da calcolare. Oltre al costruttore, la classe ha due metodi: un metodo che calcola il perimetro e uno che calcola l'area. Attributi della classe sono le misure dei tre lati a, b, c """ def __init__(self, a, b, c): """Metodo costruttore. In Python i costruttori hanno il nome speciale __init__ Controlla se i tre valori immessi sono accettabili, cioè se sono validi come misure dei lati di un triangolo. In caso contrario solleva una eccezione TriangoloImpossibile. """ if a + b > c and a + c > b and b + c > a: self.a = a self.b = b self.c = c else: raise TriangoloImpossibile def perimetro(self): """Calcola il perimetro. Ritorna un numero in virgola mobile.""" return self.a + self.b + self.c def area(self): """Calcola l'area con la formula di Erone. Ritorna un numero in virgola mobile.""" p = self.perimetro() / 2 return sqrt(p * (p-self.a) * (p-self.b) * (p-self.c)) class Finestra(Gtk.Window): """Classe che definisce l'interfaccia grafica.""" def __init__(self): Gtk.Window.__init__(self, title="Calcolo area triangolo") self.riga1 = Gtk.HBox() self.riga2 = Gtk.HBox() self.riga3 = Gtk.HBox() self.colonna = Gtk.VBox() self.label1 = Gtk.Label(label="Lato 1 ") self.label2 = Gtk.Label(label="Lato 2 ") self.label3 = Gtk.Label(label="Lato 3 ") self.entry1 = Gtk.Entry() self.entry2 = Gtk.Entry() self.entry3 = Gtk.Entry() self.button = Gtk.Button(label="Calcola") self.button.connect("clicked", self.calcola) self.risultato = Gtk.Label(label=" -- ") self.riga1.pack_start(self.label1, True, True, 0) self.riga1.pack_start(self.entry1, True, True, 0) self.riga2.pack_start(self.label2, True, True, 0) self.riga2.pack_start(self.entry2, True, True, 0) self.riga3.pack_start(self.label3, True, True, 0) self.riga3.pack_start(self.entry3, True, True, 0) self.colonna.pack_start(self.riga1, True, True, 7) self.colonna.pack_start(self.riga2, True, True, 7) self.colonna.pack_start(self.riga3, True, True, 7) self.colonna.pack_start(self.risultato, True, True, 0) self.colonna.pack_start(self.button, True, True, 3) self.add(self.colonna) def calcola(self, widget): """Acquisisce l'input, lo valida e calcola l'area.""" try: # Legge i dati inseriti in ogni casella. a = float(self.entry1.get_text()) b = float(self.entry2.get_text()) c = float(self.entry3.get_text()) triangolo = Triangolo(a, b, c) except ValueError: # Cattura l'errore di tipo di dati. self.risultato.set_text("Devi inserire valori numerici.") except TriangoloImpossibile as err: # Cattura l'errore di dati non possibili. self.risultato.set_text(err.message) else: # Nessun errore, espone il risultato. self.risultato.set_text(f"Area = {triangolo.area()}") win = Finestra() win.connect("destroy", Gtk.main_quit) win.set_resizable(False) #win.set_default_size(100,120) win.show_all() Gtk.main() }}} = PyQt = '''[[https://it.wikipedia.org/wiki/Qt_(toolkit)|Qt]]''' è un insieme di librerie usate per fornire di interfaccia grafica applicazioni e ambienti desktop (è tra l'altro parte integrante dell'ambiente grafico [[AmbienteGrafico/Kde| KDE Plasma]]). {{{#!wiki important Per la serie 5 delle librerie Qt il supporto cesserà a maggio 2023. È consigliato usare per i nuovi progetti la versione 6. }}} == Installazione == === Pacchetto deb === Per usare le librerie nella versione Qt5 [[AmministrazioneSistema/InstallareProgrammi|installare]] il pacchetto [[apt://python3-pyqt5|python3-pyqt5]]. Per usare le librerie nella versione '''Qt6''' da '''Ubuntu 22.10''' e successivi [[AmministrazioneSistema/InstallareProgrammi|installare]] il pacchetto [[apt://python3-pyqt6|python3-pyqt6]]. {{{#!wiki note Fino a '''Ubuntu 22.04''' la versione 6 della suite di pacchetti '''Python3-Py``Qt6''' non è disponibile nei [[Repository|repository]] ufficiali. }}} In alternativa possono essere installati tramite il [[https://launchpad.net/~savoury1/+archive/ubuntu/qt-6-2|PPA Qt 6.2.x - backports]], che fornisce sia versioni più recenti delle [[https://it.wikipedia.org/wiki/Qt_(toolkit)|librerie Qt6]] rispetto a quelle del repository ufficiale, sia la suite di pacchetti '''Python3-Py``Qt6''' fino a '''Ubuntu 22.04'''. 0. Digitare nel [[AmministrazioneSistema/Terminale|terminale]] il comando:{{{ sudo add-apt-repository ppa:savoury1/qt-6-2 }}} 0. Aggiornare il database dei pacchetti:{{{ sudo apt update }}} 0. [[AmministrazioneSistema/InstallareProgrammi|Installare]] il pacchetto [[apt://python3-pyqt6|python3-pyqt6]]. === Tramite pip === 0. [[AmministrazioneSistema/InstallareProgrammi|installare]] il pacchetto [[apt://python3-pip|pip3]]. 0. Digitare nel [[AmministrazioneSistema/Terminale|terminale]], in funzione della versione scelta, uno dei seguenti comandi: {{{ pip3 install pyQt5 }}} o {{{ pip3 install pyQt6 }}} === Esempio di applicazione === {{{#!python #!/usr/bin/env python3 # -*- coding: utf-8 -*- from math import sqrt # Importa la funzione sqrt (radice quadrata) dal modulo math import sys # NB questo script può funzionare sia con le PyQt6 che con le PyQt5. # Nel secondo caso bisogna sostituire nelle tre righe seguenti `PyQt6' con: `PyQt5' from PyQt6.QtCore import Qt from PyQt6 import QtGui from PyQt6.QtWidgets import (QApplication, QDialog, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QSizePolicy, QVBoxLayout ) class Ui_Dialog(object): """Classe che crea il widget contenitore di tutta l'nterfaccia grafica""" def setupUi(self, Dialog): Dialog.resize(300, 200) Dialog.setWindowTitle("Calcolo area triangolo") self.verticalLayout = QVBoxLayout(Dialog) self.gridLayout = QGridLayout() self.label_lato1 = QLabel() self.label_lato1.setText("Lato 1") self.input_lato1 = QLineEdit() self.gridLayout.addWidget(self.label_lato1, 0, 0, 1, 1) self.gridLayout.addWidget(self.input_lato1, 0, 1, 1, 1) self.label_lato2 = QLabel() self.label_lato2.setText("Lato 2") self.input_lato2 = QLineEdit() self.gridLayout.addWidget(self.label_lato2, 1, 0, 1, 1) self.gridLayout.addWidget(self.input_lato2, 1, 1, 1, 1) self.label_lato3 = QLabel() self.label_lato3.setText("Lato 3") self.input_lato3 = QLineEdit() self.gridLayout.addWidget(self.label_lato3, 2, 0, 1, 1) self.gridLayout.addWidget(self.input_lato3, 2, 1, 1, 1) self.verticalLayout.addLayout(self.gridLayout) self.esito = QLabel() sizePolicy = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.esito.sizePolicy().hasHeightForWidth()) self.esito.setSizePolicy(sizePolicy) self.esito.setAlignment(Qt.AlignmentFlag.AlignCenter) self.esito.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse) self.esito.setCursor(QtGui.QCursor(Qt.CursorShape.IBeamCursor)) font = QtGui.QFont() font.setPointSize(12) font.setBold(True) font.setWeight(75) self.esito.setFont(font) self.esito.setTextFormat(Qt.TextFormat.RichText) self.esito.setWordWrap(False) self.esito.setText(". . .") self.esito_HLayout = QHBoxLayout() self.esito_HLayout.addWidget(self.esito, Qt.AlignmentFlag.AlignCenter) self.verticalLayout.addLayout(self.esito_HLayout) self.button_horizontalLayout = QHBoxLayout() self.button_ok = QPushButton("Calcola") self.button_ok.setFixedSize(80,40) self.button_horizontalLayout.addWidget(self.button_ok, Qt.AlignmentFlag.AlignCenter) self.verticalLayout.addLayout(self.button_horizontalLayout) self.button_ok.clicked.connect(self.OK) def OK(self): self.esito.setText(self.calcoli()) def calcoli(self): """Acquisisce l'input, lo valida e calcola l'area.""" try: # Legge i dati inseriti in ogni casella. a = float(self.input_lato1.text()) b = float(self.input_lato2.text()) c = float(self.input_lato3.text()) triangolo = Triangolo(a, b, c) except ValueError: # Cattura l'errore di tipo di dati. risultato = "Devi inserire valori numerici." except TriangoloImpossibile as err: # Cattura l'errore di dati non possibili. risultato = err.message else: # Nessun errore, espone il risultato. risultato = f"Area = {triangolo.area()}" return risultato class TriangoloImpossibile(Exception): """Classe per sollevare un'eccezione se vengono immesse misure incompatibili.""" def __init__(self, message="Un triangolo con questi lati non esiste."): self.message = message class Triangolo: """Classe per costruire e validare gli oggetti triangolo da calcolare. Oltre al costruttore, la classe ha due metodi: un metodo che calcola il perimetro e uno che calcola l'area. Attributi della classe sono le misure dei tre lati a, b, c """ def __init__(self, a, b, c): """Metodo costruttore. In Python i costruttori hanno il nome speciale __init__ Controlla se i tre valori immessi sono accettabili, cioè se sono validi come misure dei lati di un triangolo. In caso contrario solleva l'eccezione TriangoloImpossibile. """ if a + b > c and a + c > b and b + c > a: self.a = a self.b = b self.c = c else: raise TriangoloImpossibile def perimetro(self): """Calcola il perimetro. Ritorna un numero in virgola mobile.""" return self.a + self.b + self.c def area(self): """Calcola l'area con la formula di Erone. Ritorna un numero in virgola mobile.""" p = self.perimetro() / 2 return sqrt(p * (p-self.a) * (p-self.b) * (p-self.c)) if __name__ == "__main__": import sys app = QApplication(sys.argv) Dialog = QDialog() ui = Ui_Dialog() ui.setupUi(Dialog) Dialog.show() sys.exit(app.exec()) }}} === Un esempio più avanzato della stessa applicazione === {{{#!wiki note Questo script funziona solo con Qt6.}}} {{{#!python #!/usr/bin/python3 # -*- coding: utf-8 -*- from math import sqrt # Importa la funzione sqrt (radice quadrata) dal modulo math import sys from PyQt6 import QtCore, QtGui from PyQt6.QtWidgets import (QApplication, QDialog, QGridLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QVBoxLayout ) INTESTAZIONE = {'input': "Inserire le misure", 'error': "Errore", 'end': "" } class Ui_Dialog(object): """Classe che crea il widget contenitore di tutta l'nterfaccia grafica""" def setupUi(self, Dialog): Dialog.resize(400, 250) Dialog.setWindowTitle("Calcolo dell'area di un triangolo") #icon = QtGui.QIcon('/percorso/di/una/icona.svg') #Dialog.setWindowIcon(icon) self.verticalLayout = QVBoxLayout(Dialog) self.gridLayout = QGridLayout() self.intestazione = QLabel(Dialog) font = QtGui.QFont() font.setPointSize(12) font.setBold(True) font.setWeight(75) self.intestazione.setFont(font) self.intestazione.setTextFormat(QtCore.Qt.TextFormat.RichText) self.intestazione.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter) self.intestazione.setText(INTESTAZIONE['input']) self.intestazione.default_ss = self.intestazione.styleSheet() # salva lo styleSheet originale self.verticalLayout.addWidget(self.intestazione) self.label_lato1 = QLabel() self.label_lato1.setText("Lato 1") self.input_lato1 = MyLineEdit("1") self.gridLayout.addWidget(self.input_lato1, 0, 0, 1, 1) self.gridLayout.addWidget(self.label_lato1, 0, 1, 1, 1) self.label_lato2 = QLabel() self.label_lato2.setText("Lato 2") self.input_lato2 = MyLineEdit("2") self.gridLayout.addWidget(self.input_lato2, 1, 0, 1, 1) self.gridLayout.addWidget(self.label_lato2, 1, 1, 1, 1) self.label_lato3 = QLabel() self.label_lato3.setText("Lato 3") self.input_lato3 = MyLineEdit("3") self.gridLayout.addWidget(self.input_lato3, 2, 0, 1, 1) self.gridLayout.addWidget(self.label_lato3, 2, 1, 1, 1) self.verticalLayout.addLayout(self.gridLayout) self.messaggio = QLabel(Dialog) self.messaggio.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.TextSelectableByMouse) self.messaggio.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.IBeamCursor)) font = QtGui.QFont() font.setPointSize(12) font.setBold(True) font.setWeight(75) self.messaggio.setFont(font) self.messaggio.setTextFormat(QtCore.Qt.TextFormat.RichText) self.messaggio.setWordWrap(False) self.messaggio.setText("") self.horizontalLayout_messaggio = QHBoxLayout() self.horizontalLayout_messaggio.addWidget(self.messaggio, QtCore.Qt.AlignmentFlag.AlignLeft) self.gridLayout.addLayout(self.horizontalLayout_messaggio, 3, 0, 1, 2) self.button_ok = QPushButton("Calcola") self.button_ok.setEnabled(False) self.button_ok.setFixedSize(80,40) self.button_cancel = QPushButton("Annulla") self.button_cancel.setFixedSize(80,40) self.button_cancel.setStyleSheet("QToolTip {background-color: #ffffbb; \ color: black; \ font: 9pt; \ border-radius: 3px}" ) self.button_cancel.setToolTip("Per uscire") self.horizontalLayout_buttons = QHBoxLayout() self.horizontalLayout_buttons.addWidget(self.button_ok, QtCore.Qt.AlignmentFlag.AlignRight ) self.horizontalLayout_buttons.addWidget(self.button_cancel, QtCore.Qt.AlignmentFlag.AlignRight ) self.gridLayout.addLayout(self.horizontalLayout_buttons, 4, 1, 1, 1) self.button_ok.clicked.connect(self.OK) self.button_cancel.clicked.connect(Dialog.reject) Dialog.setTabOrder(self.input_lato1, self.input_lato2) Dialog.setTabOrder(self.input_lato2, self.input_lato3) Dialog.setTabOrder(self.input_lato3, self.button_ok) Dialog.setTabOrder(self.button_ok, self.button_cancel) def check(self): """Viene eseguito da validazione() di ogni campo se ha input valido. Se tutti i campi sono validi, abilita il pulsante "Calcola". """ for campo in [self.input_lato1, self.input_lato2, self.input_lato3]: is_valid = getattr(campo, 'isValid') if not is_valid: break if is_valid: self.button_ok.setEnabled(True) def OK(self): self.messaggio.setText(self.calcoli()) def calcoli(self) -> str: """Acquisisce l'input, lo valida e restituisce l'area. Viene eseguito quando viene cliccato il pulsante "Calcola". Legge i dati da ogni campo e cambia il formato da it_IT.UTF-8 a C per potere convertire le stringhe in numero a virgola mobile con float(). Il formato del risultato viene restituito in it_IT.UTF-8 """ # a = float(self.input_lato1.text().replace('.', '').replace(',', '.')) b = float(self.input_lato2.text().replace('.', '').replace(',', '.')) c = float(self.input_lato3.text().replace('.', '').replace(',', '.')) try: triangolo = Triangolo(a, b, c) except TriangoloImpossibile as err: # Errore di dati incompatibili. risultato = err.message self.set_error_format() else: risultato = f"Area = {triangolo.area():.5g}".replace('.', ',') self.intestazione.setText(INTESTAZIONE['end']) return risultato def set_error_format(self): """Formatta e compila Intestazione e Risultato in caso di errore.""" self.intestazione.setStyleSheet("color: red") self.intestazione.setText(INTESTAZIONE['error']) self.messaggio.setStyleSheet("color: red") class TriangoloImpossibile(Exception): """Classe per sollevare un'eccezione se vengono immesse misure incompatibili.""" def __init__(self, message="Un triangolo con questi lati non esiste."): self.message = message class Triangolo: """Classe per costruire e validare l'oggetto triangolo da calcolare. Oltre al costruttore, la classe ha due metodi: un metodo che calcola il perimetro e uno che calcola l'area. Attributi della classe sono le misure dei tre lati a, b, c """ def __init__(self, a, b, c): """Metodo costruttore. In Python i costruttori hanno il nome speciale __init__ Controlla se i tre valori immessi sono accettabili, cioè se sono validi come misure dei lati di un triangolo. In caso contrario solleva una eccezione TriangoloImpossibile. """ if a + b > c and a + c > b and b + c > a: self.a, self.b, self.c = a, b, c else: raise TriangoloImpossibile def perimetro(self) -> float: """Calcola il perimetro.""" return self.a + self.b + self.c def area(self) -> float: """Calcola l'area con la formula di Erone.""" p = self.perimetro() / 2 return sqrt(p * (p-self.a) * (p-self.b) * (p-self.c)) class MyLineEdit(QLineEdit): """Classe per istanziare i campi di input dei lati. Il campo viene formattato durante la scrittura in base all'input digitato: ha una maschera di input che accetta solo interi o decimali >= 0,00001 L'input è giudicato valido in base al formato numerico locale: n.nnn,nnnnn Per informare di questo, un tooltip compare passando il cursore sul campo. """ def __init__(self, num): super(MyLineEdit, self).__init__() self.setStyleSheet("QToolTip {background-color: #ffffbb; \ color: black; \ font: 9pt; \ border-radius: 3px}" ) self.tooltip = "Usare la virgola come
separatore decimale" self.setToolTip(self.tooltip) self.default_ss = self.styleSheet() # salva lo styleSheet originale self.bg_colors = {'green': '#b6ffb5', 'yellow': '#ffff7d'} self.myLabel = getattr(ui, f"label_lato{num}") # serve per la formattazione self.setPlaceholderText(f"Misura del {num}° lato...") self.textChanged.connect(self.validazione) self.isValid = False # in partenza è vuoto ## validatore self.validator = QtGui.QDoubleValidator(bottom=0.00001, decimals=5) self.validator.setNotation(QtGui.QDoubleValidator.Notation.StandardNotation) self.setValidator(self.validator) def focusOutEvent(self, event): """Modifica la formattazione ogni volta che il campo perde il focus.""" super(MyLineEdit, self).focusOutEvent(event) state = self.validazione() if state is self.validator.State.Acceptable: # 2 self.default_format() else: self.invalid_format() def validazione(self): """Verifica se l'input del campo è valido o meno. Si basa sul validatore QDoubleValidator che è anche maschera di input e cioè impedisce l'immissione di caratteri non validi. Ma siccome si può cambiare campo con qualsiasi contenuto, anche non valid, comunque ritorna se la riga è valid o meno, per potere evidenziare l'errore. """ state = self.validator.validate(self.text(), 0)[0] if state is self.validator.State.Acceptable: # 2 self.isValid = True ui.check() bg_color = self.bg_colors['green'] elif state is self.validator.State.Intermediate: # 1 self.isValid = False bg_color = self.bg_colors['yellow'] self.setStyleSheet(self.default_ss + 'QLineEdit {color: black; \ background-color: %s \ }' %bg_color ) return state def default_format(self): """Ripristina lo styleSheet originale per segnalare input valido. Viene eseguito all'uscita dal campo. """ self.setStyleSheet(self.default_ss) self.myLabel.setStyleSheet(self.default_ss) def invalid_format(self): """Formatta il campo e l'etichetta per segnalare input non valido. Viene eseguito all'uscita dal campo. """ self.setStyleSheet(self.default_ss + "QLineEdit {border: 1px solid red}") self.myLabel.setStyleSheet("color: red") ui.button_ok.setEnabled(False) def focusInEvent(self, event): """Ripristina lo styleSheet originale quando si torna sul campo.""" super(MyLineEdit, self).focusInEvent(event) self.isValid = False ui.intestazione.setStyleSheet(self.default_ss) ui.intestazione.setText(INTESTAZIONE['input']) ui.messaggio.setStyleSheet(self.default_ss) ui.messaggio.setText("") self.default_format() if __name__ == "__main__": import sys app = QApplication(sys.argv) Dialog = QDialog() ui = Ui_Dialog() ui.setupUi(Dialog) Dialog.show() sys.exit(app.exec()) }}} = Tkinter = '''Tk''' è un piccolo toolkit per la creazione di interfacce grafiche che oltre a '''Python''' è stato portato anche verso altri linguaggi come '''Ruby''', '''Lisp''' etc. Per usare il programma [[AmministrazioneSistema/InstallareProgrammi|installare]] il pacchetto [[apt://python3-tk|python3-tk]]. ##Il pacchetto fornisce il modulo '''tkinter''' il quale permette l'interfacciamento di '''Python''' con '''Tk'''. === Esempio di applicazione === {{{#!python #!/usr/bin/python3 from math import sqrt # Importa la funzione sqrt (radice quadrata) dal modulo math # Importa widgets e altri attributi dal modulo per l'interfaccia grafica from tkinter import Button, Entry, Frame, Label, StringVar, E, W, N, S class TriangoloImpossibile(Exception): """Classe per sollevare un'eccezione se vengono immesse misure incompatibili.""" def __init__(self, message="Un triangolo con questi lati non esiste."): self.message = message class Triangolo: """Classe per costruire e validare gli oggetti triangolo da calcolare. Oltre al costruttore, la classe ha due metodi: un metodo che calcola il perimetro e uno che calcola l'area. Attributi della classe sono le misure dei tre lati a, b, c """ def __init__(self, a, b, c): """Metodo costruttore. In Python i costruttori hanno il nome speciale __init__ Controlla se i tre valori immessi sono accettabili, cioè se sono validi come misure dei lati di un triangolo. In caso contrario solleva una eccezione TriangoloImpossibile. """ if a + b > c and a + c > b and b + c > a: self.a = a self.b = b self.c = c else: raise TriangoloImpossibile def perimetro(self): """Calcola il perimetro. Ritorna un numero in virgola mobile.""" return self.a + self.b + self.c def area(self): """Calcola l'area con la formula di Erone. Ritorna un numero in virgola mobile.""" p = self.perimetro() / 2 return sqrt(p * (p-self.a) * (p-self.b) * (p-self.c)) class Dialogo(Frame): """Classe che gestisce l'interfaccia grafica ed esegue i calcoli.""" def __init__(self): Frame.__init__(self) self.master.title("Area triangolo.") # Diamo il titolo alla finestra. self.master.minsize(250, 150) # Dimensioni minime della finestra self.grid(sticky=E+W+N+S) self.ris = StringVar() # Questa variabile stringa viene usata per self.ris.set("---") # aggiornare la gui quando il risultato cambia. # Rende ridimensionabile la finestra dell'applicazione top = self.winfo_toplevel() top.rowconfigure(0, weight=1) top.columnconfigure(0, weight=1) for i in range(5): self.rowconfigure(i, weight=1) self.columnconfigure(1, weight=1) # Etichetta e casella di input del lato 1 self.etichetta1 = Label(self, text="Lato 1 ") self.etichetta1.grid(row=0, column=0, padx=2) self.entrata1 = Entry(self) self.entrata1.grid(row=0, column=1, pady=2, padx=2, sticky=E+W+N+S) # Etichetta e casella d'inserimento del lato 2 self.etichetta2 = Label(self, text="Lato 2 ") self.etichetta2.grid(row=1, column=0, padx=2) self.entrata2 = Entry(self) self.entrata2.grid(row=1, column=1, pady=2, padx=2, sticky=E+W+N+S) # Etichetta e casella d'inserimento del lato 3 self.etichetta3 = Label(self, text="Lato 3 ") self.etichetta3.grid(row=2, column=0, padx=2) self.entrata3 = Entry(self) self.entrata3.grid(row=2, column=1, pady=2, padx=2, sticky=E+W+N+S) # Pulsante "Calcola" self.bottone = Button(self, text="Calcola", command=self.calcola) self.bottone.grid(row=3, column=0, columnspan=2, pady=2, padx=2, sticky=E+W+N+S) # Mostra il risultato. self.risultato = Label(self, textvariable=self.ris) self.risultato.grid(row=4, column=0, columnspan=2, pady=2, padx=2, sticky=E+W+N+S) def calcola(self): """Acquisisce l'input, lo valida e calcola l'area.""" try : # Legge i dati inseriti in ogni casella. a = float(self.entrata1.get()) b = float(self.entrata2.get()) c = float(self.entrata3.get()) t = Triangolo(a, b, c) except ValueError: # Cattura l'errore di tipo di dati. self.ris.set("Devi inserire valori numerici.") except TriangoloImpossibile as err: self.ris.set(err.message) else: # Nessun errore, espone il risultato. self.ris.set(f"Area = {t.area()}") # Avvio del programma a condizione che non sia caricato come modulo if __name__=="__main__": d=Dialogo() d.mainloop() }}} = wxPython = ##Qua manca una succinta descrizione. Per usare il programma [[AmministrazioneSistema/InstallareProgrammi|installare]] il pacchetto [[apt://python3-wxgtk4.0|python3-wxgtk4.0]]. ##Il pacchetto fornisce il modulo '''wx''', il quale provvede ai collegamenti '''Python3''' alle librerie '''wxgtk''' versione 4 e seguenti. === Esempio di applicazione === {{{#!python #!/usr/bin/python3 import wx # Importa il modulo per l'interfaccia grafica from math import sqrt # Importa la funzione sqrt (radice quadrata) dal modulo math class TriangoloImpossibile(Exception): """Classe per sollevare un'eccezione se vengono immesse misure incompatibili.""" def __init__(self, message="Un triangolo con questi lati non esiste."): self.message = message class Triangolo: """Classe per costruire e validare gli oggetti triangolo da calcolare. Oltre al costruttore, la classe ha due metodi: un metodo che calcola il perimetro e uno che calcola l'area. Attributi della classe sono le misure dei tre lati a, b, c """ def __init__(self, a, b, c): """Metodo costruttore. In Python i costruttori hanno il nome speciale __init__ Controlla se i tre valori immessi sono accettabili, cioè se sono validi come misure dei lati di un triangolo. In caso contrario solleva una eccezione TriangoloImpossibile. """ if a + b > c and a + c > b and b + c > a: self.a = a self.b = b self.c = c else: raise TriangoloImpossibile def perimetro(self): """Calcola il perimetro. Ritorna un numero in virgola mobile.""" return self.a + self.b + self.c def area(self): """Calcola l'area con la formula di Erone. Ritorna un numero in virgola mobile.""" p = self.perimetro() / 2 return sqrt(p * (p-self.a) * (p-self.b) * (p-self.c)) class Example(wx.Frame): """Classe che gestisce l'interfaccia grafica ed esegue i calcoli.""" def __init__(self, parent, title): super(Example,self).__init__(parent, title=title, size=(400,300)) self.InitUI() def calcola(self, evt): """Acquisisce l'input, lo valida e calcola l'area.""" try: # legge i dati inseriti in ogni casella a = float(self.latoa.GetValue()) b = float(self.latob.GetValue()) c = float(self.latoc.GetValue()) triangolo = Triangolo(a, b, c) except ValueError: # cattura l'errore di tipo di dati avviso = "Bisogna inserire valori numerici." _ = wx.MessageBox( avviso, 'Errore', wx.OK | wx.ICON_ERROR) except TriangoloImpossibile as err: # cattura l'errore di dati non possibili _ = wx.MessageBox(err.message, 'Errore', wx.OK | wx.ICON_ERROR) else: # Nessun errore, espone il risultato avviso =f" L'area del triangolo misura {triangolo.area()}" _ = wx.MessageBox(avviso, 'Risultato', wx.OK ) def InitUI(self): """Metodo che definisce l'interfaccia grafica.""" font = wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT) self.panel = wx.Panel(self) #self.panel.SetBackgroundColour("red") self.lato1 = wx.StaticText(self.panel, label='Lato 1') self.lato1.SetFont(font) #self.lato1.SetForegroundColour((0, 0, 0)) self.lato1.SetPosition((10, 10)) self.latoa = wx.TextCtrl(self.panel) self.latoa.SetFont(font) self.latoa.SetPosition((60, 6)) self.lato2 = wx.StaticText(self.panel, label='Lato 2') self.lato2.SetFont(font) #self.lato2.SetForegroundColour((0, 0, 0)) self.lato2.SetPosition((10, 50)) self.latob = wx.TextCtrl(self.panel) self.latob.SetFont(font) self.latob.SetPosition((60, 45)) self.lato3 = wx.StaticText(self.panel, label='Lato 3') self.lato3.SetFont(font) #self.lato3.SetForegroundColour((0, 0, 0)) self.lato3.SetPosition((10, 90)) self.latoc = wx.TextCtrl(self.panel) self.latoc.SetFont(font) self.latoc.SetPosition((60, 85)) self.bottone = wx.Button(self.panel, label = 'Calcola') self.bottone.SetPosition((310, 235)) self.Bind(wx.EVT_BUTTON, self.calcola, self.bottone) def main(): app = wx.App() ex = Example(None, title = "Calcolo area triangolo") ex.Show() app.MainLoop() if __name__ == '__main__': main() }}} = Ulteriori risorse = * [[http://wiki.python.org/moin/GuiProgramming|Lungo elenco di framework e toolkit per creare GUI con Python]] * [[https://python-gtk-3-tutorial.readthedocs.io/en/latest/|Tutorial per Python GTK+ 3]] * [[https://doc.qt.io/qtforpython/#documentation|Documentazione di Qt per Python]] * [[https://docs.python.org/3/library/tkinter.html|Documentazione di tkinter]] * [[https://docs.wxpython.org/|Documentazione di wxPython]] * [[http://www.python.it/|Sito di riferimento per i programmatori Python italiani]] * [[http://www.python.org/|Il sito ufficiale del linguaggio Python]] ---- CategoryProgrammazione