Dimensione: 5301
Commento:
|
← Versione 23 del 06/02/2023 20.05.01 ⇥
Dimensione: 36470
Commento: piccola correzione del codice nell'esempio più avanzato con Qt
|
Le cancellazioni sono segnalate in questo modo. | Le aggiunte sono segnalate in questo modo. |
Linea 1: | Linea 1: |
## page was renamed from mtramonti2007/provaGUIPython3 | |
Linea 7: | Linea 8: |
<<Informazioni(forum="https://forum.ubuntu-it.org/viewtopic.php?f=46&t=638223"; rilasci="18.04")>> | <<Informazioni(forum="https://forum.ubuntu-it.org/viewtopic.php?f=46&t=638223"; rilasci="18.04 20.04 22.04")>> |
Linea 11: | Linea 12: |
All'interno di questa pagina sono presenti degli esempi di come creare delle semplici interfacce grafiche usando programmi come '''wxPython''', '''Tkinter''' e '''PyGTK'''. Tkinter è di norma preinstallato in Ubuntu, mentre wxPython può essere installato con: {{{ sudo apt install python3-wxgtk4.0 }}} = Tkinter = Tk è un piccolo toolkit per la creazione d'interfacce grafiche è stato successivamente portato anche verso altri linguaggi (Python, Ruby, Lisp, ...). Tkinter è il modulo Python che permette l'interfacciamento con Tk. == Esempio di applicazione == Di seguito viene mostrato un esempio di programma: |
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.<<BR>> 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]]<<BR>> 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]].<<BR>> 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 === |
Linea 29: | Linea 32: |
# Importiamo la funzione sqrt (radice quadrata) dal modulo math from math import sqrt # Importiamo i widgets dal modulo Tkinter from tkinter import * # Definiamo una classe Triangolo con tre metodi: # un costruttore, un metodo che calcola il perimetro e uno che calcola l'area del triangolo. |
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 |
Linea 37: | Linea 45: |
# Attributi della classe (le misure dei tre lati) a, b, c = 0, 0, 0 # Metodo costruttore def __init__(self,a,b,c): # Controlliamo se i tre valori dati possono effettivamente essere le misure dei lati di un triangolo. # Caso contrario solleviamo una bella eccezione! if ((a+b>c) and (a+c>b) and (b+c>a)): self.a=a self.b=b self.c=c else: # abbiamo dato un nome ed un essaggio di errore all'eccezione raise "TriangoloImpossibile"("I lati non possono formare un triangolo") # Metodo che calcola il perimetro def perimetro(self): return self.a + self.b + self.c # Metodo che calcola l'area (tramite formula di Erone) def area(self): p=self.perimetro()/2.0 return sqrt(p*(p-self.a)*(p-self.b)*(p-self.c)) # questa e' la classe che definisce l'interfaccia grafica |
"""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<br>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)) |
Linea 65: | Linea 675: |
def __init__(self): |
"""Classe che gestisce l'interfaccia grafica ed esegue i calcoli.""" def __init__(self): |
Linea 69: | Linea 680: |
self.master.minsize(250,150) # Dimensioni minime della finestra | self.master.minsize(250, 150) # Dimensioni minime della finestra |
Linea 72: | Linea 683: |
self.ris=StringVar() # Questa variabile stringa viene usata per self.ris.set("---") # aggiornare la gui quando il risultato cambia. # Rendiamo ridimensionabile la finestra dell'applicazione top=self.winfo_toplevel() |
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() |
Linea 80: | Linea 691: |
for i in range(5): self.rowconfigure(i, weight=1) | for i in range(5): self.rowconfigure(i, weight=1) |
Linea 83: | Linea 695: |
self.etichetta1=Label(self, text="Lato 1 ") # Etichetta del lato 1 self.etichetta1.grid(row=0, column=0, padx=2) self.entrata1=Entry(self) # Casella d'inserimento del lato 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) |
Linea 90: | Linea 701: |
self.etichetta2=Label(self, text="Lato 2 ") # Etichetta del lato 2 | # Etichetta e casella d'inserimento del lato 2 self.etichetta2 = Label(self, text="Lato 2 ") |
Linea 92: | Linea 704: |
self.entrata2=Entry(self) # Casella d'inserimento del lato 2 |
self.entrata2 = Entry(self) |
Linea 96: | Linea 707: |
self.etichetta3=Label(self, text="Lato 3 ") # Etichetta del lato 3 | # Etichetta e casella d'inserimento del lato 3 self.etichetta3 = Label(self, text="Lato 3 ") |
Linea 98: | Linea 710: |
self.entrata3=Entry(self) # Casella d'inserimento del lato 3 |
self.entrata3 = Entry(self) |
Linea 102: | Linea 713: |
self.bottone=Button(self, text="Calcola", command=self.calcola) # Bottone "Calcola" | # Pulsante "Calcola" self.bottone = Button(self, text="Calcola", command=self.calcola) |
Linea 105: | Linea 717: |
self.risultato=Label(self, textvariable=self.ris) # Testo che motra il risultato. | # Mostra il risultato. self.risultato = Label(self, textvariable=self.ris) |
Linea 108: | Linea 721: |
# Raccogliamo l'input e calcoliamo l'area def calcola(self): try : a=float(self.entrata1.get()) b=float(self.entrata2.get()) c=float(self.entrata3.get()) t=Triangolo(a,b,c) self.ris.set("Area = "+str(t.area())) except ValueError: self.ris.set("Devi inserire valori numerici.") except "TriangoloImpossibile": self.ris.set("I lati immessi non possono formare un triangolo") |
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()}") |
Linea 123: | Linea 737: |
d=Dialogo() d.mainloop() }}} = PyGTK = |
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() }}} |
Linea 134: | Linea 870: |
* [[http://wiki.python.org/moin/GuiProgramming|Programmare interfacce grafiche con Python]] * [[http://www.wxpython.org|Sito ufficiale di wxpython]] * [[http://www.pygtk.org/|Sito ufficiale di PyGTK]] * [[http://www.tcl.tk/|Sito degli sviluppatori tcl/tk]] * [[http://www.python.it/|Sito di riferimento per i programmatori python italiani]] * [[http://www.python.org/|Il sito ufficiale del linguaggio python]] |
* [[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]] |
Guida verificata con Ubuntu: 20.04 22.04
Problemi in questa pagina? Segnalali in questa discussione
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 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.
GTK
GTK+ è un toolkit per la creazione di interfacce grafiche che è stato progettato inizialmente come ausilio alla programmazione per GIMP ed è diventato una parte fondamentale dell'ambiente GNOME.
Il programma è preinstallato in Ubuntu. Nel caso fosse stato inavvertitamente rimosso o mancasse installare il pacchetto python3-gi.
Esempio di applicazione
1 #!/usr/bin/python3
2
3 import gi
4 gi.require_version('Gtk', '3.0')
5 from gi.repository import Gtk # Importa il modulo per l'interfaccia grafica
6 from math import sqrt # Importa la funzione sqrt (radice quadrata) dal modulo math
7
8 class TriangoloImpossibile(Exception):
9 """Classe per sollevare un'eccezione se vengono immesse misure incompatibili."""
10
11 def __init__(self, message="Un triangolo con questi lati non esiste."):
12 self.message = message
13
14
15 class Triangolo:
16 """Classe per costruire e validare gli oggetti triangolo da calcolare.
17
18 Oltre al costruttore, la classe ha due metodi:
19 un metodo che calcola il perimetro e uno che calcola l'area.
20 Attributi della classe sono le misure dei tre lati a, b, c
21 """
22
23 def __init__(self, a, b, c):
24 """Metodo costruttore. In Python i costruttori hanno il nome speciale __init__
25
26 Controlla se i tre valori immessi sono accettabili,
27 cioè se sono validi come misure dei lati di un triangolo.
28 In caso contrario solleva una eccezione TriangoloImpossibile.
29 """
30 if a + b > c and a + c > b and b + c > a:
31 self.a = a
32 self.b = b
33 self.c = c
34 else:
35 raise TriangoloImpossibile
36
37 def perimetro(self):
38 """Calcola il perimetro. Ritorna un numero in virgola mobile."""
39 return self.a + self.b + self.c
40
41 def area(self):
42 """Calcola l'area con la formula di Erone. Ritorna un numero in virgola mobile."""
43 p = self.perimetro() / 2
44 return sqrt(p * (p-self.a) * (p-self.b) * (p-self.c))
45
46
47 class Finestra(Gtk.Window):
48 """Classe che definisce l'interfaccia grafica."""
49
50 def __init__(self):
51 Gtk.Window.__init__(self, title="Calcolo area triangolo")
52
53 self.riga1 = Gtk.HBox()
54 self.riga2 = Gtk.HBox()
55 self.riga3 = Gtk.HBox()
56 self.colonna = Gtk.VBox()
57
58 self.label1 = Gtk.Label(label="Lato 1 ")
59 self.label2 = Gtk.Label(label="Lato 2 ")
60 self.label3 = Gtk.Label(label="Lato 3 ")
61 self.entry1 = Gtk.Entry()
62 self.entry2 = Gtk.Entry()
63 self.entry3 = Gtk.Entry()
64 self.button = Gtk.Button(label="Calcola")
65 self.button.connect("clicked", self.calcola)
66 self.risultato = Gtk.Label(label=" -- ")
67
68 self.riga1.pack_start(self.label1, True, True, 0)
69 self.riga1.pack_start(self.entry1, True, True, 0)
70 self.riga2.pack_start(self.label2, True, True, 0)
71 self.riga2.pack_start(self.entry2, True, True, 0)
72 self.riga3.pack_start(self.label3, True, True, 0)
73 self.riga3.pack_start(self.entry3, True, True, 0)
74
75 self.colonna.pack_start(self.riga1, True, True, 7)
76 self.colonna.pack_start(self.riga2, True, True, 7)
77 self.colonna.pack_start(self.riga3, True, True, 7)
78 self.colonna.pack_start(self.risultato, True, True, 0)
79 self.colonna.pack_start(self.button, True, True, 3)
80 self.add(self.colonna)
81
82 def calcola(self, widget):
83 """Acquisisce l'input, lo valida e calcola l'area."""
84 try: # Legge i dati inseriti in ogni casella.
85 a = float(self.entry1.get_text())
86 b = float(self.entry2.get_text())
87 c = float(self.entry3.get_text())
88 triangolo = Triangolo(a, b, c)
89 except ValueError: # Cattura l'errore di tipo di dati.
90 self.risultato.set_text("Devi inserire valori numerici.")
91 except TriangoloImpossibile as err: # Cattura l'errore di dati non possibili.
92 self.risultato.set_text(err.message)
93 else: # Nessun errore, espone il risultato.
94 self.risultato.set_text(f"Area = {triangolo.area()}")
95
96 win = Finestra()
97 win.connect("destroy", Gtk.main_quit)
98 win.set_resizable(False)
99 #win.set_default_size(100,120)
100 win.show_all()
101 Gtk.main()
PyQt
Qt è un insieme di librerie usate per fornire di interfaccia grafica applicazioni e ambienti desktop (è tra l'altro parte integrante dell'ambiente grafico KDE Plasma).
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 installare il pacchetto python3-pyqt5.
Per usare le librerie nella versione Qt6 da Ubuntu 22.10 e successivi installare il pacchetto python3-pyqt6.
Fino a Ubuntu 22.04 la versione 6 della suite di pacchetti Python3-PyQt6 non è disponibile nei repository ufficiali.
In alternativa possono essere installati tramite il PPA Qt 6.2.x - backports, che fornisce sia versioni più recenti delle librerie Qt6 rispetto a quelle del repository ufficiale, sia la suite di pacchetti Python3-PyQt6 fino a Ubuntu 22.04.
Digitare nel terminale il comando:
sudo add-apt-repository ppa:savoury1/qt-6-2
Aggiornare il database dei pacchetti:
sudo apt update
Installare il pacchetto python3-pyqt6.
Tramite pip
installare il pacchetto pip3.
Digitare nel terminale, in funzione della versione scelta, uno dei seguenti comandi:
pip3 install pyQt5
o
pip3 install pyQt6
Esempio di applicazione
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3
4 from math import sqrt # Importa la funzione sqrt (radice quadrata) dal modulo math
5 import sys
6 # NB questo script può funzionare sia con le PyQt6 che con le PyQt5.
7 # Nel secondo caso bisogna sostituire nelle tre righe seguenti `PyQt6' con: `PyQt5'
8 from PyQt6.QtCore import Qt
9 from PyQt6 import QtGui
10 from PyQt6.QtWidgets import (QApplication, QDialog, QGridLayout,
11 QHBoxLayout, QLabel, QLineEdit,
12 QPushButton, QSizePolicy, QVBoxLayout
13 )
14
15 class Ui_Dialog(object):
16 """Classe che crea il widget contenitore di tutta l'nterfaccia grafica"""
17 def setupUi(self, Dialog):
18 Dialog.resize(300, 200)
19 Dialog.setWindowTitle("Calcolo area triangolo")
20 self.verticalLayout = QVBoxLayout(Dialog)
21 self.gridLayout = QGridLayout()
22
23 self.label_lato1 = QLabel()
24 self.label_lato1.setText("Lato 1")
25 self.input_lato1 = QLineEdit()
26 self.gridLayout.addWidget(self.label_lato1, 0, 0, 1, 1)
27 self.gridLayout.addWidget(self.input_lato1, 0, 1, 1, 1)
28
29 self.label_lato2 = QLabel()
30 self.label_lato2.setText("Lato 2")
31 self.input_lato2 = QLineEdit()
32 self.gridLayout.addWidget(self.label_lato2, 1, 0, 1, 1)
33 self.gridLayout.addWidget(self.input_lato2, 1, 1, 1, 1)
34
35 self.label_lato3 = QLabel()
36 self.label_lato3.setText("Lato 3")
37 self.input_lato3 = QLineEdit()
38 self.gridLayout.addWidget(self.label_lato3, 2, 0, 1, 1)
39 self.gridLayout.addWidget(self.input_lato3, 2, 1, 1, 1)
40
41 self.verticalLayout.addLayout(self.gridLayout)
42
43 self.esito = QLabel()
44 sizePolicy = QSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding)
45 sizePolicy.setHorizontalStretch(0)
46 sizePolicy.setVerticalStretch(0)
47 sizePolicy.setHeightForWidth(self.esito.sizePolicy().hasHeightForWidth())
48 self.esito.setSizePolicy(sizePolicy)
49 self.esito.setAlignment(Qt.AlignmentFlag.AlignCenter)
50 self.esito.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
51 self.esito.setCursor(QtGui.QCursor(Qt.CursorShape.IBeamCursor))
52 font = QtGui.QFont()
53 font.setPointSize(12)
54 font.setBold(True)
55 font.setWeight(75)
56 self.esito.setFont(font)
57 self.esito.setTextFormat(Qt.TextFormat.RichText)
58 self.esito.setWordWrap(False)
59 self.esito.setText(". . .")
60
61 self.esito_HLayout = QHBoxLayout()
62 self.esito_HLayout.addWidget(self.esito, Qt.AlignmentFlag.AlignCenter)
63 self.verticalLayout.addLayout(self.esito_HLayout)
64
65 self.button_horizontalLayout = QHBoxLayout()
66 self.button_ok = QPushButton("Calcola")
67 self.button_ok.setFixedSize(80,40)
68 self.button_horizontalLayout.addWidget(self.button_ok, Qt.AlignmentFlag.AlignCenter)
69 self.verticalLayout.addLayout(self.button_horizontalLayout)
70 self.button_ok.clicked.connect(self.OK)
71
72
73 def OK(self):
74 self.esito.setText(self.calcoli())
75
76 def calcoli(self):
77 """Acquisisce l'input, lo valida e calcola l'area."""
78
79 try: # Legge i dati inseriti in ogni casella.
80 a = float(self.input_lato1.text())
81 b = float(self.input_lato2.text())
82 c = float(self.input_lato3.text())
83 triangolo = Triangolo(a, b, c)
84 except ValueError: # Cattura l'errore di tipo di dati.
85 risultato = "Devi inserire valori numerici."
86 except TriangoloImpossibile as err: # Cattura l'errore di dati non possibili.
87 risultato = err.message
88 else: # Nessun errore, espone il risultato.
89 risultato = f"Area = {triangolo.area()}"
90 return risultato
91
92
93 class TriangoloImpossibile(Exception):
94 """Classe per sollevare un'eccezione se vengono immesse misure incompatibili."""
95
96 def __init__(self, message="Un triangolo con questi lati non esiste."):
97 self.message = message
98
99
100 class Triangolo:
101 """Classe per costruire e validare gli oggetti triangolo da calcolare.
102
103 Oltre al costruttore, la classe ha due metodi:
104 un metodo che calcola il perimetro e uno che calcola l'area.
105 Attributi della classe sono le misure dei tre lati a, b, c
106 """
107
108 def __init__(self, a, b, c):
109 """Metodo costruttore. In Python i costruttori hanno il nome speciale __init__
110
111 Controlla se i tre valori immessi sono accettabili,
112 cioè se sono validi come misure dei lati di un triangolo.
113 In caso contrario solleva l'eccezione TriangoloImpossibile.
114 """
115 if a + b > c and a + c > b and b + c > a:
116 self.a = a
117 self.b = b
118 self.c = c
119 else:
120 raise TriangoloImpossibile
121
122 def perimetro(self):
123 """Calcola il perimetro. Ritorna un numero in virgola mobile."""
124 return self.a + self.b + self.c
125
126 def area(self):
127 """Calcola l'area con la formula di Erone. Ritorna un numero in virgola mobile."""
128 p = self.perimetro() / 2
129 return sqrt(p * (p-self.a) * (p-self.b) * (p-self.c))
130
131
132 if __name__ == "__main__":
133 import sys
134 app = QApplication(sys.argv)
135 Dialog = QDialog()
136 ui = Ui_Dialog()
137 ui.setupUi(Dialog)
138 Dialog.show()
139 sys.exit(app.exec())
Un esempio più avanzato della stessa applicazione
Questo script funziona solo con Qt6.
1 #!/usr/bin/python3
2 # -*- coding: utf-8 -*-
3
4 from math import sqrt # Importa la funzione sqrt (radice quadrata) dal modulo math
5 import sys
6 from PyQt6 import QtCore, QtGui
7 from PyQt6.QtWidgets import (QApplication, QDialog, QGridLayout, QHBoxLayout,
8 QLabel, QLineEdit, QPushButton, QVBoxLayout
9 )
10
11 INTESTAZIONE = {'input': "Inserire le misure",
12 'error': "Errore",
13 'end': ""
14 }
15
16 class Ui_Dialog(object):
17 """Classe che crea il widget contenitore di tutta l'nterfaccia grafica"""
18
19 def setupUi(self, Dialog):
20 Dialog.resize(400, 250)
21 Dialog.setWindowTitle("Calcolo dell'area di un triangolo")
22 #icon = QtGui.QIcon('/percorso/di/una/icona.svg')
23 #Dialog.setWindowIcon(icon)
24 self.verticalLayout = QVBoxLayout(Dialog)
25 self.gridLayout = QGridLayout()
26
27 self.intestazione = QLabel(Dialog)
28 font = QtGui.QFont()
29 font.setPointSize(12)
30 font.setBold(True)
31 font.setWeight(75)
32 self.intestazione.setFont(font)
33 self.intestazione.setTextFormat(QtCore.Qt.TextFormat.RichText)
34 self.intestazione.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
35 self.intestazione.setText(INTESTAZIONE['input'])
36 self.intestazione.default_ss = self.intestazione.styleSheet() # salva lo styleSheet originale
37 self.verticalLayout.addWidget(self.intestazione)
38
39 self.label_lato1 = QLabel()
40 self.label_lato1.setText("Lato 1")
41 self.input_lato1 = MyLineEdit("1")
42 self.gridLayout.addWidget(self.input_lato1, 0, 0, 1, 1)
43 self.gridLayout.addWidget(self.label_lato1, 0, 1, 1, 1)
44
45 self.label_lato2 = QLabel()
46 self.label_lato2.setText("Lato 2")
47 self.input_lato2 = MyLineEdit("2")
48 self.gridLayout.addWidget(self.input_lato2, 1, 0, 1, 1)
49 self.gridLayout.addWidget(self.label_lato2, 1, 1, 1, 1)
50
51 self.label_lato3 = QLabel()
52 self.label_lato3.setText("Lato 3")
53 self.input_lato3 = MyLineEdit("3")
54 self.gridLayout.addWidget(self.input_lato3, 2, 0, 1, 1)
55 self.gridLayout.addWidget(self.label_lato3, 2, 1, 1, 1)
56
57 self.verticalLayout.addLayout(self.gridLayout)
58
59 self.messaggio = QLabel(Dialog)
60 self.messaggio.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.TextSelectableByMouse)
61 self.messaggio.setCursor(QtGui.QCursor(QtCore.Qt.CursorShape.IBeamCursor))
62 font = QtGui.QFont()
63 font.setPointSize(12)
64 font.setBold(True)
65 font.setWeight(75)
66 self.messaggio.setFont(font)
67 self.messaggio.setTextFormat(QtCore.Qt.TextFormat.RichText)
68 self.messaggio.setWordWrap(False)
69 self.messaggio.setText("")
70 self.horizontalLayout_messaggio = QHBoxLayout()
71 self.horizontalLayout_messaggio.addWidget(self.messaggio, QtCore.Qt.AlignmentFlag.AlignLeft)
72
73 self.gridLayout.addLayout(self.horizontalLayout_messaggio, 3, 0, 1, 2)
74
75 self.button_ok = QPushButton("Calcola")
76 self.button_ok.setEnabled(False)
77 self.button_ok.setFixedSize(80,40)
78 self.button_cancel = QPushButton("Annulla")
79 self.button_cancel.setFixedSize(80,40)
80 self.button_cancel.setStyleSheet("QToolTip {background-color: #ffffbb; \
81 color: black; \
82 font: 9pt; \
83 border-radius: 3px}"
84 )
85 self.button_cancel.setToolTip("Per uscire")
86 self.horizontalLayout_buttons = QHBoxLayout()
87 self.horizontalLayout_buttons.addWidget(self.button_ok,
88 QtCore.Qt.AlignmentFlag.AlignRight
89 )
90 self.horizontalLayout_buttons.addWidget(self.button_cancel,
91 QtCore.Qt.AlignmentFlag.AlignRight
92 )
93 self.gridLayout.addLayout(self.horizontalLayout_buttons, 4, 1, 1, 1)
94 self.button_ok.clicked.connect(self.OK)
95 self.button_cancel.clicked.connect(Dialog.reject)
96
97 Dialog.setTabOrder(self.input_lato1, self.input_lato2)
98 Dialog.setTabOrder(self.input_lato2, self.input_lato3)
99 Dialog.setTabOrder(self.input_lato3, self.button_ok)
100 Dialog.setTabOrder(self.button_ok, self.button_cancel)
101
102
103 def check(self):
104 """Viene eseguito da validazione() di ogni campo se ha input valido.
105
106 Se tutti i campi sono validi, abilita il pulsante "Calcola".
107 """
108 for campo in [self.input_lato1, self.input_lato2, self.input_lato3]:
109 is_valid = getattr(campo, 'isValid')
110 if not is_valid:
111 break
112 if is_valid:
113 self.button_ok.setEnabled(True)
114
115
116 def OK(self):
117 self.messaggio.setText(self.calcoli())
118
119
120 def calcoli(self) -> str:
121 """Acquisisce l'input, lo valida e restituisce l'area.
122
123 Viene eseguito quando viene cliccato il pulsante "Calcola".
124 Legge i dati da ogni campo e cambia il formato da it_IT.UTF-8 a C
125 per potere convertire le stringhe in numero a virgola mobile
126 con float().
127 Il formato del risultato viene restituito in it_IT.UTF-8
128 """
129 #
130 a = float(self.input_lato1.text().replace('.', '').replace(',', '.'))
131 b = float(self.input_lato2.text().replace('.', '').replace(',', '.'))
132 c = float(self.input_lato3.text().replace('.', '').replace(',', '.'))
133 try:
134 triangolo = Triangolo(a, b, c)
135 except TriangoloImpossibile as err: # Errore di dati incompatibili.
136 risultato = err.message
137 self.set_error_format()
138 else:
139 risultato = f"Area = {triangolo.area():.5g}".replace('.', ',')
140 self.intestazione.setText(INTESTAZIONE['end'])
141 return risultato
142
143 def set_error_format(self):
144 """Formatta e compila Intestazione e Risultato in caso di errore."""
145 self.intestazione.setStyleSheet("color: red")
146 self.intestazione.setText(INTESTAZIONE['error'])
147 self.messaggio.setStyleSheet("color: red")
148
149
150 class TriangoloImpossibile(Exception):
151 """Classe per sollevare un'eccezione se vengono immesse misure incompatibili."""
152
153 def __init__(self, message="Un triangolo con questi lati non esiste."):
154 self.message = message
155
156
157 class Triangolo:
158 """Classe per costruire e validare l'oggetto triangolo da calcolare.
159
160 Oltre al costruttore, la classe ha due metodi:
161 un metodo che calcola il perimetro e uno che calcola l'area.
162 Attributi della classe sono le misure dei tre lati a, b, c
163 """
164
165 def __init__(self, a, b, c):
166 """Metodo costruttore. In Python i costruttori hanno il nome speciale __init__
167
168 Controlla se i tre valori immessi sono accettabili,
169 cioè se sono validi come misure dei lati di un triangolo.
170 In caso contrario solleva una eccezione TriangoloImpossibile.
171 """
172 if a + b > c and a + c > b and b + c > a:
173 self.a, self.b, self.c = a, b, c
174 else:
175 raise TriangoloImpossibile
176
177 def perimetro(self) -> float:
178 """Calcola il perimetro."""
179 return self.a + self.b + self.c
180
181 def area(self) -> float:
182 """Calcola l'area con la formula di Erone."""
183 p = self.perimetro() / 2
184 return sqrt(p * (p-self.a) * (p-self.b) * (p-self.c))
185
186
187 class MyLineEdit(QLineEdit):
188 """Classe per istanziare i campi di input dei lati.
189
190 Il campo viene formattato durante la scrittura in base all'input digitato:
191 ha una maschera di input che accetta solo interi o decimali >= 0,00001
192 L'input è giudicato valido in base al formato numerico locale: n.nnn,nnnnn
193 Per informare di questo, un tooltip compare passando il cursore sul campo.
194 """
195
196 def __init__(self, num):
197 super(MyLineEdit, self).__init__()
198 self.setStyleSheet("QToolTip {background-color: #ffffbb; \
199 color: black; \
200 font: 9pt; \
201 border-radius: 3px}"
202 )
203 self.tooltip = "Usare la virgola come<br>separatore decimale"
204 self.setToolTip(self.tooltip)
205 self.default_ss = self.styleSheet() # salva lo styleSheet originale
206 self.bg_colors = {'green': '#b6ffb5', 'yellow': '#ffff7d'}
207 self.myLabel = getattr(ui, f"label_lato{num}") # serve per la formattazione
208 self.setPlaceholderText(f"Misura del {num}° lato...")
209 self.textChanged.connect(self.validazione)
210 self.isValid = False # in partenza è vuoto
211 ## validatore
212 self.validator = QtGui.QDoubleValidator(bottom=0.00001, decimals=5)
213 self.validator.setNotation(QtGui.QDoubleValidator.Notation.StandardNotation)
214 self.setValidator(self.validator)
215
216
217 def focusOutEvent(self, event):
218 """Modifica la formattazione ogni volta che il campo perde il focus."""
219 super(MyLineEdit, self).focusOutEvent(event)
220 state = self.validazione()
221 if state is self.validator.State.Acceptable: # 2
222 self.default_format()
223 else:
224 self.invalid_format()
225
226
227 def validazione(self):
228 """Verifica se l'input del campo è valido o meno.
229
230 Si basa sul validatore QDoubleValidator che è anche maschera di input
231 e cioè impedisce l'immissione di caratteri non validi. Ma siccome si
232 può cambiare campo con qualsiasi contenuto, anche non valid, comunque
233 ritorna se la riga è valid o meno, per potere evidenziare l'errore.
234 """
235 state = self.validator.validate(self.text(), 0)[0]
236 if state is self.validator.State.Acceptable: # 2
237 self.isValid = True
238 ui.check()
239 bg_color = self.bg_colors['green']
240 elif state is self.validator.State.Intermediate: # 1
241 self.isValid = False
242 bg_color = self.bg_colors['yellow']
243 self.setStyleSheet(self.default_ss + 'QLineEdit {color: black; \
244 background-color: %s \
245 }' %bg_color
246 )
247 return state
248
249
250 def default_format(self):
251 """Ripristina lo styleSheet originale per segnalare input valido.
252
253 Viene eseguito all'uscita dal campo.
254 """
255 self.setStyleSheet(self.default_ss)
256 self.myLabel.setStyleSheet(self.default_ss)
257
258
259 def invalid_format(self):
260 """Formatta il campo e l'etichetta per segnalare input non valido.
261
262 Viene eseguito all'uscita dal campo.
263 """
264 self.setStyleSheet(self.default_ss + "QLineEdit {border: 1px solid red}")
265 self.myLabel.setStyleSheet("color: red")
266 ui.button_ok.setEnabled(False)
267
268
269 def focusInEvent(self, event):
270 """Ripristina lo styleSheet originale quando si torna sul campo."""
271 super(MyLineEdit, self).focusInEvent(event)
272 self.isValid = False
273 ui.intestazione.setStyleSheet(self.default_ss)
274 ui.intestazione.setText(INTESTAZIONE['input'])
275 ui.messaggio.setStyleSheet(self.default_ss)
276 ui.messaggio.setText("")
277 self.default_format()
278
279 if __name__ == "__main__":
280 import sys
281 app = QApplication(sys.argv)
282 Dialog = QDialog()
283 ui = Ui_Dialog()
284 ui.setupUi(Dialog)
285 Dialog.show()
286 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 installare il pacchetto python3-tk.
Esempio di applicazione
1 #!/usr/bin/python3
2
3 from math import sqrt # Importa la funzione sqrt (radice quadrata) dal modulo math
4
5 # Importa widgets e altri attributi dal modulo per l'interfaccia grafica
6 from tkinter import Button, Entry, Frame, Label, StringVar, E, W, N, S
7
8
9 class TriangoloImpossibile(Exception):
10 """Classe per sollevare un'eccezione se vengono immesse misure incompatibili."""
11
12 def __init__(self, message="Un triangolo con questi lati non esiste."):
13 self.message = message
14
15
16 class Triangolo:
17 """Classe per costruire e validare gli oggetti triangolo da calcolare.
18
19 Oltre al costruttore, la classe ha due metodi:
20 un metodo che calcola il perimetro e uno che calcola l'area.
21 Attributi della classe sono le misure dei tre lati a, b, c
22 """
23
24 def __init__(self, a, b, c):
25 """Metodo costruttore. In Python i costruttori hanno il nome speciale __init__
26
27 Controlla se i tre valori immessi sono accettabili,
28 cioè se sono validi come misure dei lati di un triangolo.
29 In caso contrario solleva una eccezione TriangoloImpossibile.
30 """
31 if a + b > c and a + c > b and b + c > a:
32 self.a = a
33 self.b = b
34 self.c = c
35 else:
36 raise TriangoloImpossibile
37
38 def perimetro(self):
39 """Calcola il perimetro. Ritorna un numero in virgola mobile."""
40 return self.a + self.b + self.c
41
42 def area(self):
43 """Calcola l'area con la formula di Erone. Ritorna un numero in virgola mobile."""
44 p = self.perimetro() / 2
45 return sqrt(p * (p-self.a) * (p-self.b) * (p-self.c))
46
47
48 class Dialogo(Frame):
49 """Classe che gestisce l'interfaccia grafica ed esegue i calcoli."""
50
51 def __init__(self):
52 Frame.__init__(self)
53 self.master.title("Area triangolo.") # Diamo il titolo alla finestra.
54 self.master.minsize(250, 150) # Dimensioni minime della finestra
55 self.grid(sticky=E+W+N+S)
56
57 self.ris = StringVar() # Questa variabile stringa viene usata per
58 self.ris.set("---") # aggiornare la gui quando il risultato cambia.
59
60 # Rende ridimensionabile la finestra dell'applicazione
61 top = self.winfo_toplevel()
62 top.rowconfigure(0, weight=1)
63 top.columnconfigure(0, weight=1)
64
65 for i in range(5):
66 self.rowconfigure(i, weight=1)
67 self.columnconfigure(1, weight=1)
68
69 # Etichetta e casella di input del lato 1
70 self.etichetta1 = Label(self, text="Lato 1 ")
71 self.etichetta1.grid(row=0, column=0, padx=2)
72 self.entrata1 = Entry(self)
73 self.entrata1.grid(row=0, column=1, pady=2, padx=2, sticky=E+W+N+S)
74
75 # Etichetta e casella d'inserimento del lato 2
76 self.etichetta2 = Label(self, text="Lato 2 ")
77 self.etichetta2.grid(row=1, column=0, padx=2)
78 self.entrata2 = Entry(self)
79 self.entrata2.grid(row=1, column=1, pady=2, padx=2, sticky=E+W+N+S)
80
81 # Etichetta e casella d'inserimento del lato 3
82 self.etichetta3 = Label(self, text="Lato 3 ")
83 self.etichetta3.grid(row=2, column=0, padx=2)
84 self.entrata3 = Entry(self)
85 self.entrata3.grid(row=2, column=1, pady=2, padx=2, sticky=E+W+N+S)
86
87 # Pulsante "Calcola"
88 self.bottone = Button(self, text="Calcola", command=self.calcola)
89 self.bottone.grid(row=3, column=0, columnspan=2, pady=2, padx=2, sticky=E+W+N+S)
90
91 # Mostra il risultato.
92 self.risultato = Label(self, textvariable=self.ris)
93 self.risultato.grid(row=4, column=0, columnspan=2, pady=2, padx=2, sticky=E+W+N+S)
94
95 def calcola(self):
96 """Acquisisce l'input, lo valida e calcola l'area."""
97 try : # Legge i dati inseriti in ogni casella.
98 a = float(self.entrata1.get())
99 b = float(self.entrata2.get())
100 c = float(self.entrata3.get())
101 t = Triangolo(a, b, c)
102 except ValueError: # Cattura l'errore di tipo di dati.
103 self.ris.set("Devi inserire valori numerici.")
104 except TriangoloImpossibile as err:
105 self.ris.set(err.message)
106 else: # Nessun errore, espone il risultato.
107 self.ris.set(f"Area = {t.area()}")
108
109 # Avvio del programma a condizione che non sia caricato come modulo
110 if __name__=="__main__":
111 d=Dialogo()
112 d.mainloop()
wxPython
Per usare il programma installare il pacchetto python3-wxgtk4.0.
Esempio di applicazione
1 #!/usr/bin/python3
2
3 import wx # Importa il modulo per l'interfaccia grafica
4 from math import sqrt # Importa la funzione sqrt (radice quadrata) dal modulo math
5
6
7 class TriangoloImpossibile(Exception):
8 """Classe per sollevare un'eccezione se vengono immesse misure incompatibili."""
9
10 def __init__(self, message="Un triangolo con questi lati non esiste."):
11 self.message = message
12
13
14 class Triangolo:
15 """Classe per costruire e validare gli oggetti triangolo da calcolare.
16
17 Oltre al costruttore, la classe ha due metodi:
18 un metodo che calcola il perimetro e uno che calcola l'area.
19 Attributi della classe sono le misure dei tre lati a, b, c
20 """
21
22 def __init__(self, a, b, c):
23 """Metodo costruttore. In Python i costruttori hanno il nome speciale __init__
24
25 Controlla se i tre valori immessi sono accettabili,
26 cioè se sono validi come misure dei lati di un triangolo.
27 In caso contrario solleva una eccezione TriangoloImpossibile.
28 """
29 if a + b > c and a + c > b and b + c > a:
30 self.a = a
31 self.b = b
32 self.c = c
33 else:
34 raise TriangoloImpossibile
35
36 def perimetro(self):
37 """Calcola il perimetro. Ritorna un numero in virgola mobile."""
38 return self.a + self.b + self.c
39
40 def area(self):
41 """Calcola l'area con la formula di Erone. Ritorna un numero in virgola mobile."""
42 p = self.perimetro() / 2
43 return sqrt(p * (p-self.a) * (p-self.b) * (p-self.c))
44
45
46 class Example(wx.Frame):
47 """Classe che gestisce l'interfaccia grafica ed esegue i calcoli."""
48
49 def __init__(self, parent, title):
50 super(Example,self).__init__(parent, title=title, size=(400,300))
51 self.InitUI()
52
53 def calcola(self, evt):
54 """Acquisisce l'input, lo valida e calcola l'area."""
55 try: # legge i dati inseriti in ogni casella
56 a = float(self.latoa.GetValue())
57 b = float(self.latob.GetValue())
58 c = float(self.latoc.GetValue())
59 triangolo = Triangolo(a, b, c)
60 except ValueError: # cattura l'errore di tipo di dati
61 avviso = "Bisogna inserire valori numerici."
62 _ = wx.MessageBox( avviso, 'Errore', wx.OK | wx.ICON_ERROR)
63 except TriangoloImpossibile as err: # cattura l'errore di dati non possibili
64 _ = wx.MessageBox(err.message, 'Errore', wx.OK | wx.ICON_ERROR)
65 else: # Nessun errore, espone il risultato
66 avviso =f" L'area del triangolo misura {triangolo.area()}"
67 _ = wx.MessageBox(avviso, 'Risultato', wx.OK )
68
69 def InitUI(self):
70 """Metodo che definisce l'interfaccia grafica."""
71 font = wx.SystemSettings.GetFont(wx.SYS_SYSTEM_FONT)
72 self.panel = wx.Panel(self)
73 #self.panel.SetBackgroundColour("red")
74 self.lato1 = wx.StaticText(self.panel, label='Lato 1')
75 self.lato1.SetFont(font)
76 #self.lato1.SetForegroundColour((0, 0, 0))
77 self.lato1.SetPosition((10, 10))
78
79 self.latoa = wx.TextCtrl(self.panel)
80 self.latoa.SetFont(font)
81 self.latoa.SetPosition((60, 6))
82
83 self.lato2 = wx.StaticText(self.panel, label='Lato 2')
84 self.lato2.SetFont(font)
85 #self.lato2.SetForegroundColour((0, 0, 0))
86 self.lato2.SetPosition((10, 50))
87
88 self.latob = wx.TextCtrl(self.panel)
89 self.latob.SetFont(font)
90 self.latob.SetPosition((60, 45))
91
92 self.lato3 = wx.StaticText(self.panel, label='Lato 3')
93 self.lato3.SetFont(font)
94 #self.lato3.SetForegroundColour((0, 0, 0))
95 self.lato3.SetPosition((10, 90))
96
97 self.latoc = wx.TextCtrl(self.panel)
98 self.latoc.SetFont(font)
99 self.latoc.SetPosition((60, 85))
100
101 self.bottone = wx.Button(self.panel, label = 'Calcola')
102 self.bottone.SetPosition((310, 235))
103 self.Bind(wx.EVT_BUTTON, self.calcola, self.bottone)
104
105
106 def main():
107 app = wx.App()
108 ex = Example(None, title = "Calcolo area triangolo")
109 ex.Show()
110 app.MainLoop()
111
112 if __name__ == '__main__':
113 main()