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.

  1. Digitare nel terminale il comando:

    sudo add-apt-repository ppa:savoury1/qt-6-2
  2. Aggiornare il database dei pacchetti:

    sudo apt update
  3. Installare il pacchetto python3-pyqt6.

Tramite pip

  1. installare il pacchetto pip3.

  2. 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()

Ulteriori risorse


CategoryProgrammazione

Programmazione/Python/InterfacceGrafichePython3 (l'ultima modifica è del 06/02/2023 20.05.01, fatta da rai)