Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Librerie di widget custom per Qt Creator

Link copiato negli appunti

Il meccanismo di promozione di widget in Qt Creator, discusso nella lezione precedente, è il modo più semplice di introdurre l'uso di widget custom nel design di una finestra.

Per ragioni che risulteranno più chiare nel seguito, questa modalità di integrazione non solo è più che sufficiente a soddisfare le esigenze di sviluppo di applicazioni basate su Qt Widgets, ma è anche la più semplice in termini di manutenibilità.

Tuttavia, la promozione è particolarmente limitante per quanto riguarda le funzionalità RAD di Qt Designer. Ad esempio, gli attributi di una widget custom non vengono renderizzati come quelli di widget nativa del framework, né tanto meno le sue proprietà sono ispezionabili nella GUI del designer.

In casi particolari, come ad esempio lo sviluppo di una libreria di widget custom, potrebbe essere richiesto di garantire agli utenti della libreria un livello di integrazione con le funzionalità del RAD pari a quello offerto dalle widget native.

In questi casi è possibile soddisfare questo requisito mediante la definizione di un apposito plugin per Qt Creator che consente di aggiungere una o più widget abilitandone il corretto disegno e la gestione delle proprietà direttamente nell'ambiente RAD di Qt.

Un plugin di widget per Qt Creator non è altro che una libreria contenente i file header e di implementazione delle nostre widget ed espone API specifiche per l'inizializzazione, il disegno e la configurazione delle widget da parte del designer.

In questa lezione, creeremo un progetto in Qt Creator per la definizione di una libreria di widget basata sulla classe Led discussa in precedenza. Esso sarà corredato da un altro progetto che implementa una semplice applicazione desktop dimostrativa.

Per semplicità, faremo uso di un progetto subdir che faccia da contenitore ai due progetti sopra citati, di modo da gestirli in maniera unificata.

Creazione del progetto

Per la creazione del progetto contenitore, dopo aver avviato Qt Creator, è necessario cliccare su New -> New file or project e selezionare il template Subdirs Project sotto la categoria Other Project dall'elenco di template.

Si può a questo punto dare un nome al progetto, ad esempio "Led" e completare i passaggi del wizard, come di consueto. La procedura di creazione di un progetto subdirs, nelle ultime versioni di Qt Creator, termina con la richiesta di definizione di un sotto progetto. Per le versioni più vecchie è possibile aggiungere un sotto progetto facendo click con il tasto destro del mouse sul nome del progetto contenitore e selezionando la voce New subproject.

Per la creazione della applicazione dimostrativa sceglieremo quindi il template Qt Widgets Application nella categoria Application cui daremo il nome App, mentre per la creazione della libreria di widget sceglieremo il template Qt Custom Designer Widget appositamente creato per questo scopo, cui daremo il nome ledWidgets.

Il wizard di creazione di librerie di widget custom include passaggi specifici, tra cui l'inserimento dell'elenco di widget custom che vogliamo facciamo parte della libreria. In questo caso ci basta aggiungere un solo elemento di nome Led e lasciare che il wizard popoli i parametri con valori di default, come mostrato nella figura seguente. L'utilità di molte di queste impostazioni sarà chiarita nel seguito.

Figura 1. (click per ingrandire)

Una volta terminata la fase di inizializzazione del progetto, la vista ad albero dei file di progetto dovrebbe essere simile a quella riportata nella figura seguente:

Figura 2. (click per ingrandire)

Modifiche al codice sorgente della widget

Come prima cosa possiamo copiare il codice sorgente della classe Led nei file corrispondenti led.h e led.cpp all'interno del progetto ledWidgets ed apportare alcune piccole modifiche al solo header file della classe che sono funzionali alla corretta definizione del plugin:

#ifndef LED_H
#define LED_H
// include la macro QDESIGNER_WIDGET_EXPORT
#include <QtDesigner/QtDesigner>
#include <QWidget>
// decorazione della classe Led con la macro QDESIGNER_WIDGET_EXPORT
class QDESIGNER_WIDGET_EXPORT Led : public QWidget
{
    Q_OBJECT
    // proprietà editabili dal designer
    Q_PROPERTY(bool On READ isOn WRITE setOn)
    Q_PROPERTY(QColor LightColor READ getLightColor WRITE setLightColor)
public:
    Led(QWidget *parent = 0);
    bool isOn() const;
    QColor getLightColor() const;
public slots:
    void setOn(bool value);
    void setLightColor(const QColor& c);
protected:
    virtual void paintEvent(QPaintEvent *event) override;
    bool on;
    QColor lightColor;
    QGraphicsDropShadowEffect* glowEffect;
};
#endif

Le modifiche apportate alla classe sono evidenziate da commenti, il resto del codice, incluso il file di implementazione led.cpp resta del tutto invariato. La decorazione della classe Led con la macro QDESIGNER_WIDGET_EXPORT serve a garantire la corretta esportazione della classe come simbolo per ogni piattaforma supportata da Qt Widgets.

L'uso della macro Q_PROPERTY serve a contrassegnare le proprietà che saranno ispezionabili ed editabili da designer. In questo caso, servono a registrare i metodo getter e setter per i membri protetti della classe nel meta-object system di Qt, di modo sia possibile modificare lo stato della widget disegnata dal designer.

Implementazione alle classi plugin

Apportate le modifiche alla classe Led, possiamo concentrarci sulla implementazione delle API che servono a gestire i metadati e la corretta inizializzazione della widget da parte del designer.

Tali funzionalità sono espletate mediante la definizione di un'apposita classe plugin per ogni widget custom. Per convenzione, il nome di queste classi è del tipo NomeWidgetPlugin.

Avendo basato la creazione della libreria sul template predefinito da Qt, al progetto è stata aggiunta automaticamente la classe denominata LedPlugin, derivata dalla classe base QDesignerCustomWidgetInterface.

La definizione della classe LedPlugin è contenuta nel seguente listato:

#ifndef LEDPLUGIN_H
#define LEDPLUGIN_H
#include <QtUiPlugin/QDesignerCustomWidgetInterface>
class LedPlugin : public QObject, public QDesignerCustomWidgetInterface
{
    Q_OBJECT
    Q_INTERFACES(QDesignerCustomWidgetInterface)
#if QT_VERSION >= 0x050000
    Q_PLUGIN_METADATA(IID "org.qtguide.example.led")
#endif // QT_VERSION >= 0x050000
public:
    LedPlugin(QObject *parent = 0);
    bool isContainer() const;
    bool isInitialized() const;
    QIcon icon() const;
    QString domXml() const;
    QString group() const;
    QString includeFile() const;
    QString name() const;
    QString toolTip() const;
    QString whatsThis() const;
    QWidget *createWidget(QWidget *parent);
    void initialize(QDesignerFormEditorInterface *core);
private:
    bool m_initialized;
};
#endif

In questo caso è importante sottolineare l'uso della macro Q_PLUGIN_METADATA per assegnare al plugin un identificativo univoco. Per evitare di incorrere in collisioni, si adotta una convenzione simile a quella usata per attribuire nomi ai packages in Java, in questo caso org.qtguide.example.led.

Da specifica, la macro Q_PLUGIN_METADATA dovrebbe apparire una sola volta in tutto il codice sorgente della libreria. Poichè in questo esempio, la libreria contiene una sola widget custom, il wizard include la macro nel codice della classe LedPlugin. Il setup di un progetto che contiene più di una widget, prevede l'aggiunta di un'ulteriore classe derivata da QDesignerCustomWidgetCollectionInterface responsabile di enumerare tutte le classi plugin presenti nella libreria.

Per questo motivo, se volessimo espandere la libreria con altre widget, a parte la definizione di tale classe, dovremmo aver cura di rimuovere la macro Q_PLUGIN_METADATA presente nella classe LedPlugin.

I metodi della classe LedPlugin sono invocati dal designer per mostrare il nome della widget, il gruppo o categoria cui essa appartiene, un'icona ed il nome del file header che contiene la definizione della widget e l'elenco di proprietà ispezionabili dal designer, come visto in precedenza.

L'implementazione generata automaticamente in fase di inizializzazione del progetto è sufficiente a far funzionare il plugin correttamente. Tuttavia è possibile apportare alcune modifiche, ad esempio per definire il gruppo e l'icona che il designer userà per enumerare la widget nel suo elenco, come mostrato nel frammento seguente:

// ledplugin.cpp
...
// categoria della widget
QString LedPlugin::group() const
{
    return QLatin1String("Led widgets");
}
// icona
QIcon LedPlugin::icon() const
{
    // l'icona è un file di nome led.png incluso come risorsa binaria all'interno del file icons.qrc,
    // aggiunto automaticamente al progetto in fase di inizializzazione
    return QIcon(":/icons/led.png");
}
// header file della classe Led
QString LedPlugin::includeFile() const
{
    return QLatin1String("led.h");
}
...

Compilazione e deployment della libreria

I paragrafi precedenti esauriscono le modifiche da apportare al codice della libreria al fine di garantire il corretto funzionamento del plugin. Tuttavia, la sola compilazione del progetto non è sufficiente ad integrare la nostra widget nell'ambiente RAD di Qt Creator.

Si rende infatti necessario provvedere all'istallazione del plugin nella sotto cartella predefinita da Qt Creator per l'enumerazione dei plugin disponibili in fase di avvio.

Le impostazioni relative alla compilazione ed al deployment della libreria sono contenute nel file di progetto libWidgets.pro, i cui contenuti sono mostrati di seguito:

CONFIG      += plugin debug_and_release
TARGET      = $$qtLibraryTarget(ledplugin)
TEMPLATE    = lib
HEADERS     = ledplugin.h
SOURCES     = ledplugin.cpp
RESOURCES   = icons.qrc
LIBS        += -L. 
greaterThan(QT_MAJOR_VERSION, 4) {
    QT += designer
} else {
    CONFIG += designer
}
target.path = $$[QT_INSTALL_PLUGINS]/designer
INSTALLS    += target
include(led.pri)

In particolare il nome della libreria è dato dal valore della variabile TARGET, dato dalla funzione $$qtLibraryTarget(ledplugin). Quest'ultima appende al suo argomento la desinenza "_d" o "_debug" rispettivamente su Windows e macOS alla versione contenente anche i simboli di debug. L'argomento della funzione è il questo caso il nome dell'unica classe plugin presente nel progetto. Per librerie con molteplici widget, esso sarebbe invece il nome della classe derivata da QDesignerCustomWidgetCollectionInterface.

La variabile CONFIG definisce la tipologia del progetto mediante gli attributi "plugin" e "designer" e predispone che la libreria sia sempre compilata sia in modalità debug che release.

Le variabili target.path e INSTALLS, sono invece usate per impostare il percorso di installazione della libreria, che in questo caso è la sotto cartella designer dell'applicazione Qt Creator.

Infine, la direttiva include(led.pri) assicura che i file led.h e led.cpp vengano inclusi nel processo di compilazione.

In seguito alla compilazione della libreria, per effettuare l'installazione è necessario lanciare il comando make install all'interno della cartella che contiene i binari della libreria con privilegi di amministratore, ad esempio utilizzando sudo su Linux o macOS.

Inoltre, affinchè il plugin venga rilevato e caricato correttamente da Qt Creator è necessario che vengano soddisfatti i seguenti requisiti:

  • La versione di Qt Creator e quella del Qt SDK usato come kit di sviluppo per la libreria devono coincidere.
  • La libreria deve essere compilata in versione release, come garantito dalla clausola debug_and_release.

Se tutti i requisiti sono soddisfatti, chiudendo e riavviando Qt Creator dovremmo essere in grado di visualizzare la nostra libreria nell'elenco di plugin disponibili da Tools -> Form editor -> About Qt Designer Plugins.

Figura 3. (click per ingrandire)

Applicazione demo

Possiamo a questo punto concentrarci sull'implementazione dell'applicazione dimostrativa. Essendo essa basata sul template Qt Widgets Application, la classe MainWindow è per default la finestra principale dell'applicazione.

Per modificare il layout della finestra possiamo aprire l'ambiente RAD di Qt Creator facendo doppio click con il tasto sinistro del mouse sul file mainwindow.ui.

Selezioniamo dall'elenco di widget la classe Led sotto la categoria "Led Widgets", come visto nel paragrafo precedente. Possiamo anche usare il filtro di ricerca per agevolare questa operazione

A questo punto possiamo usare la widget Led come se fosse una delle widget predefinite del framework, posizionandola in un semplice layout e modificandone le proprietà direttamente dall'ambiente RAD, come mostrato in figura:

Figura 4. (click per ingrandire)

Inoltre, passando alla modalità di editing di segnali e slot possiamo anche associare lo stato del pulsante On/Off a quello del led in modalità grafica, senza apportare alcuna modifica al codice sorgente della classe MainWindow:

Figura 5. (click per ingrandire)

Osservazioni

La creazione di plugin per Qt Creator consente senza dubbio di sfruttare al meglio le potenzialità dell'ambiente RAD anche nel caso di widget custom.

Tuttavia, questo procedimento è consigliabile solo nel caso in cui si stia effettivamente implementando una libreria di widget. In effetti, ricorrere all'implementazione di un plugin per l'uso sporadico di poche widget potrebbe comportare un certo sovraccarico nelle operazioni di progetto, soprattutto se il codice delle widget è soggetto a modifiche o non è propriamente testato.

Ti consigliamo anche