Wiki Ubuntu-it

Indice
Partecipa
FAQ
Wiki Blog
------------------
Ubuntu-it.org
Forum
Chiedi
Chat
Cerca
Planet
  • Pagina non alterabile
  • Informazioni
  • Allegati
  • Differenze per "Programmazione/Python/InterfacceGrafichePython3"
Differenze tra le versioni 21 e 22
Versione 21 del 03/03/2020 17.18.35
Dimensione: 14258
Autore: wilecoyote
Commento:
Versione 22 del 02/02/2023 10.50.22
Dimensione: 36506
Autore: wilecoyote
Commento:
Le cancellazioni sono segnalate in questo modo. Le aggiunte sono segnalate in questo modo.
Linea 8: Linea 8:
<<Informazioni(forum="https://forum.ubuntu-it.org/viewtopic.php?f=46&t=638223"; rilasci="18.04 16.04 14.04")>> <<Informazioni(forum="https://forum.ubuntu-it.org/viewtopic.php?f=46&t=638223"; rilasci="18.04 20.04 22.04")>>
Linea 12: 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
}}}

 * tutto ciò che viene scritto preceduto dal carattere # è considerato dall'interprete come un commento.
 * 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]]

= PyGTK =

'''GTK+''' è un toolkit per la creazione di interfacce grafiche è stato progettato inizialmente come ausilio alla programmazione per [[Grafica/Gimp|GIMP]] ed è diventato una parte fondamentale dell'ambiente [[AmbienteGrafico/Gnome| GNOME]].

'''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.
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 30:
#!/usr/bin/python3
Linea 31: Linea 34:
from gi.repository import Gtk
from math import sqrt
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 35: Linea 45:
    a, b, c = 0, 0, 0

    # Metodo costruttore, in python i costruttori hanno il nome speciale __init__
    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:
            raise "TriangoloImpossibile"("I lati non possono formare un triangolo")
            # abbiamo dato un nome ed un essaggio di errore all'eccezione
    # Metodo che calcola il perimetro
    """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
Linea 51: Linea 67:
        """Calcola il perimetro. Ritorna un numero in virgola mobile."""
Linea 53: Linea 70:
    # Metodo che calcola l'area (tramite formula di Erone)
Linea 55: Linea 71:
        p=self.perimetro()/2.0
        return sqrt(p*(p-self.a)*(p-self.b)*(p-self.c))
    
        """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 59: Linea 77:
    """Classe che definisce l'interfaccia grafica."""
Linea 60: Linea 80:
        Gtk.Window.__init__(self, title = "Calcolo area triangolo")
        
        Gtk.Window.__init__(self, title="Calcolo area triangolo")
Linea 66: Linea 86:
         
        self.label1 = Gtk.Label(label = "Lato 1 ")
        self.label2 = Gtk.Label(label = "Lato 2 ")
        self.label3 = Gtk.Label(label = "Lato 3 ")

        self.label1 = Gtk.Label(label="Lato 1 ")
        self.label2 = Gtk.Label(label="Lato 2 ")
        self.label3 = Gtk.Label(label="Lato 3 ")
Linea 74: Linea 93:
        self.button = Gtk.Button(label = "Calcola")         self.button = Gtk.Button(label="Calcola")
Linea 76: Linea 95:
        self.risultato = Gtk.Label(" -- ")         self.risultato = Gtk.Label(label=" -- ")
Linea 84: Linea 103:
    
Linea 87: Linea 106:
        self.colonna.pack_start(self.riga3, True, True, 7)             self.colonna.pack_start(self.riga3, True, True, 7)
Linea 92: Linea 111:

             
Linea 96: Linea 112:
        try:
            a = float(self.entry1.get_text()) # legge i dati inseriti nella 1 casella
        """Acquisisce l'input, lo valida e calcola l'area."""
        try: # Legge i dati inseriti in ogni casella.
            a = float(self.entry1.get_text())
Linea 101: Linea 118:
            self.risultato.set_text(str(triangolo.area()))
        except ValueError: # catturo l'eccezione
        except ValueError: # Cattura l'errore di tipo di dati.
Linea 104: Linea 120:
        except "TriangoloImpossibile": # catturo la seconda eccezione
            self.risultato.set_text("I lati immessi non possono formare un triangolo")

        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()}")
Linea 115: Linea 131:
}}}


= Tkinter =

'''Tk''' è un piccolo toolkit per la creazione di interfacce grafiche è stato successivamente portato anche verso altri linguaggi come '''Python''', '''Ruby''', '''Lisp''', ecc.. '''Tkinter''' è il modulo '''Python''' che permette l'interfacciamento con '''Tk'''.

== Esempio di applicazione ==

Di seguito viene mostrato un esempio di programma:

}}}


= 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.}}}
Linea 127: Linea 327:

# 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.
# -*- 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

Linea 136: Linea 483:

      # 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 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, *args):
        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'}
        for arg in args:
            self.myLabel = getattr(ui, f"label_lato{arg}") # serve per la formattazione
            self.setPlaceholderText(f"Misura del {arg}° 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 164: Linea 676:

     def __init__(self):
    """Classe che gestisce l'interfaccia grafica ed esegue i calcoli."""

    def __init__(self):
Linea 168: Linea 681:
        self.master.minsize(250,150) # Dimensioni minime della finestra         self.master.minsize(250, 150) # Dimensioni minime della finestra
Linea 171: Linea 684:
        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 179: Linea 692:
        for i in range(5): self.rowconfigure(i, weight=1)         for i in range(5):
           
self.rowconfigure(i, weight=1)
Linea 182: Linea 696:

        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 189: Linea 702:
        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 191: Linea 705:

self.entrata2=Entry(self) # Casella d'inserimento del lato 2
        self.entrata2 = Entry(self)
Linea 195: Linea 708:
        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 197: Linea 711:

self.entrata3=Entry(self) # Casella d'inserimento del lato 3
        self.entrata3 = Entry(self)
Linea 201: Linea 714:
        self.bottone=Button(self, text="Calcola", command=self.calcola) # Bottone "Calcola"         # Pulsante "Calcola"
self.bottone = Button(self, text="Calcola", command=self.calcola)
Linea 204: Linea 718:
        self.risultato=Label(self, textvariable=self.ris) # Testo che motra il risultato.         # Mostra il risultato.
self.risultato = Label(self, textvariable=self.ris)
Linea 207: Linea 722:

     # 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 222: Linea 738:
   d=Dialogo()
   d.mainloop()
  d=Dialogo()
    d.mainloop()
Linea 228: Linea 745:
Questa è una versione del programma `AreaTriangolo2.py`, presente all'interno di [[Programmazione/Python/ListatiPython3#areatraingolo2| questa pagina]], che utilizza le librerie '''wxpython'''.

== Esempio di applicazione ==
##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 ===
Linea 234: Linea 755:
import wx #importiamo l'interfaccia grafica

from string import digits # importiamo una variabile per il controllo dei dati in input
from math import sqrt # importiamo la funzione sqrt per il calcolo della radicequadrata dal modulo math
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))
Linea 240: Linea 799:
        def __init__(self, parent, title):
                super(Example,self).__init__(parent, title = title, size = (400,300))
                self.InitUI()

        def calcola(self, evt):
                a=str(self.latoa.GetValue())
                b=str(self.latob.GetValue())
                c=str(self.latoc.GetValue())
        
                
vero = self.Contronum(a)
                if vero
== "F":
                       
avviso = "Il lato a ="+ a+" <-- non e' di tipo numerico; Riprova "
                        msg = wx.MessageBox( avviso
, 'Errore',wx.OK | wx.ICON_ERROR)                            return
                vero
= self.Contronum(b)
                if vero == "F":
                        avviso = "Il lato b ="+ b+" <-- non e' di tipo numerico; Riprova "
                        ms
g = wx.MessageBox( avviso, 'Errore',wx.OK | wx.ICON_ERROR)                           return
                vero = self.Contron
um(c)
                if vero == "F":
            
avviso = "Il lato c ="+ c+" <-- non e' di tipo numerico; Riprova "
                        ms
g = wx.MessageBox( avviso, 'Errore',wx.OK | wx.ICON_ERROR)
                        return
                # li trasforma in virgola mobile e poi verifica che corrispondano ad un triangolo
                a = float(a)
                b = float(b)
                c = float(c)
        
                p = (a+b+c)/2.0
        
                if p <=a or p <= b or p <= c:
                        avviso ='I dati immessi non sono quelli di un triangolo; Riprova'
                        msg = wx.MessageBox( avviso, 'Errore',wx.OK | wx.ICON_ERROR)
                        return
                else:
                        # calcolo area triangolo
                        area =sqrt(p*(p-a)*(p-b)*(p-c))
                        avviso =" L'area del triangolo misura Mq "+str(area)
                        msg = wx.MessageBox( avviso, 'Risultato',wx.OK )
                
                        return
    
def InitUI(self):
                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 Contronum(self,valore): # serve per la verifica che i dati immessi siano di tipo numerico

                vero = "F"
                for i in range(len(valore)):

                        if valore[i] in digits :

                                vero = "T"

                                continue

                        else:

                                vero = "F"

                                break
                return vero
    """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
            a
vviso = "Bisogna inserire valori numerici."
            _
= wx.MessageBox( avviso, 'Errore', wx.OK | wx.ICON_ERROR)
        except TriangoloImpossibile as err: # cat
tura l'errore di dati non possibili
            _
= wx.MessageBox(err.message, 'Errore', wx.OK | wx.ICON_ERROR)
        else: # Ness
un 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)
Linea 337: Linea 859:
     app = wx.App()
     ex = Example(None, title = "Calcolo area triangolo")
        ex.Show()
     app.MainLoop()
    app = wx.App()
    ex = Example(None, title = "Calcolo area triangolo")
    ex.Show()
    app.MainLoop()
Linea 343: Linea 865:
     main()     main()
Linea 348: Linea 871:
 * [[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.

  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, *args):
 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         for arg in args:
 208             self.myLabel = getattr(ui, f"label_lato{arg}")  # serve per la formattazione
 209             self.setPlaceholderText(f"Misura del {arg}° lato...")
 210         self.textChanged.connect(self.validazione)
 211         self.isValid = False #  in partenza è vuoto
 212         ## validatore
 213         self.validator = QtGui.QDoubleValidator(bottom=0.00001, decimals=5)
 214         self.validator.setNotation(QtGui.QDoubleValidator.Notation.StandardNotation)
 215         self.setValidator(self.validator)
 216         
 217 
 218     def focusOutEvent(self, event):
 219         """Modifica la formattazione ogni volta che il campo perde il focus."""
 220         super(MyLineEdit, self).focusOutEvent(event)
 221         state = self.validazione()
 222         if state is self.validator.State.Acceptable: # 2
 223             self.default_format()
 224         else:
 225             self.invalid_format()
 226 
 227 
 228     def validazione(self):
 229         """Verifica se l'input del campo è valido o meno.
 230 
 231         Si basa sul validatore QDoubleValidator che è anche maschera di input
 232         e cioè impedisce l'immissione di caratteri non validi. Ma siccome si
 233         può cambiare campo con qualsiasi contenuto, anche non valid, comunque
 234         ritorna se la riga è valid o meno, per potere evidenziare l'errore.
 235         """
 236         state = self.validator.validate(self.text(), 0)[0]
 237         if state is self.validator.State.Acceptable: # 2
 238             self.isValid = True
 239             ui.check()
 240             bg_color = self.bg_colors['green']
 241         elif state is self.validator.State.Intermediate: # 1
 242             self.isValid = False
 243             bg_color = self.bg_colors['yellow']
 244         self.setStyleSheet(self.default_ss + 'QLineEdit {color: black; \
 245                                                          background-color: %s \
 246                                                          }' %bg_color
 247                            )
 248         return state
 249     
 250         
 251     def default_format(self):
 252         """Ripristina lo styleSheet originale per segnalare input valido.
 253         
 254         Viene eseguito all'uscita dal campo.
 255         """
 256         self.setStyleSheet(self.default_ss)
 257         self.myLabel.setStyleSheet(self.default_ss)
 258         
 259         
 260     def invalid_format(self):
 261         """Formatta il campo e l'etichetta per segnalare input non valido.
 262         
 263         Viene eseguito all'uscita dal campo.
 264         """
 265         self.setStyleSheet(self.default_ss + "QLineEdit {border: 1px solid red}")
 266         self.myLabel.setStyleSheet("color: red")
 267         ui.button_ok.setEnabled(False)
 268 
 269 
 270     def focusInEvent(self, event):
 271         """Ripristina lo styleSheet originale quando si torna sul campo."""
 272         super(MyLineEdit, self).focusInEvent(event)
 273         self.isValid = False
 274         ui.intestazione.setStyleSheet(self.default_ss)
 275         ui.intestazione.setText(INTESTAZIONE['input'])
 276         ui.messaggio.setStyleSheet(self.default_ss)
 277         ui.messaggio.setText("")
 278         self.default_format()
 279 
 280 if __name__ == "__main__":
 281     import sys
 282     app = QApplication(sys.argv)
 283     Dialog = QDialog()
 284     ui = Ui_Dialog()
 285     ui.setupUi(Dialog)
 286     Dialog.show()
 287     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