Cet article constitue un regroupement de notes prises pour la préparation de la certification « Qt Essentials Curriculum Block - version 1.1 ». Ces notes proviennent de différentes sources, indiquées dans le chapitre « Références » et organisées selon les différents items proposés dans la page « Qt Essentials Curriculum Block ».
Pour rappel, les personnes qui passent la certification s'engagent à ne pas divulguer d'informations sur cette certification. Ces notes ont donc étaient rassemblées avant le passage de l’examen et il ne sera pas possible de répondre aux questions par la suite.
Quels sont les différentes API natives ? Limitations ? Avantages ?
Bonne question...
...
Installation du SDK ? Compilation d'un main avec window ?
#include <QApplication>
#include <QLabel>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QLabel *label = new QLabel("Hello Qt!");
label->show();
return app.exec();
}
Nom des objets Qt : commence par un Q ; nécessite include
Compilation :
From a command prompt, change the directory to hello, and type
qmake -project
to create a platform-independent project file (hello.pro), and then type
qmake hello.pro
make // ou nmake sur VS
Run it by typing hello on Windows, ./hello on Unix, and open hello.app on Mac OS X
Widget avec texte en HTML :
QLabel *label = new QLabel("
Hello
Qt!
");
Changement de styles :
./age -style motif
Buddy = widget qui accepte le focus lors que l'on utilise le raccourci clavier d'un label :
label = new QLabel(tr("Find &what:"));
lineEdit = new QLineEdit;
label->setBuddy(lineEdit);
Gestion de la position des widgets = to manage the geometry of widgets in a window
a widget is a visual element in a user interface,Buttons, menus, scroll bars, and frames are all examples of widgets, peut contenir d'autres widgets, noms : QMenuBar, a few QToolBars, a QstatusBar dans Qt.
Documentation officielle sur Qt Creator (F1), en ligne (doc.qt.nokia.com/) ou Qt Assistant ou dans doc/html
Relation d'héritage entre les objets, accessible dans l'aide avec les lignes Inherits (parent) et Inherited by (enfants)
Basé sur les méta-objets : introspection (permet d'avoir des informations sur un objet au runtime, permet les propriétés, la traduction des chaînes de caractères, l'utilisation des scripts) et signaux/slots
Mécanisme :
Q_OBJECT
signaux/slots et connect()
tr() → permet la traduction des chaînes
parent-enfant : suppression des objets en mémoire et suppression des widgets à l'écran
QObject utilise un système de parent-enfant pour supprimer automatiquement les objets liés à un objet. Minimise les risques de fuite mémoire
Principe :
Les objets à détruire sont ceux créé avec new et sans parents :
layout manager = active automatiquement une relation parent-enfant
QApplication app(argv, argc); // sur la pile
{
label = new QPushButton(tr("&Find"));
button = new QPushButton(label, tr("&Find")); // parent-enfant
}
delete label;
// delete button; → non
Moc → Qt meta-object compiler = compile les .h pour convertir les macro (Q_OBJECT, signals, slots, etc.) ; génère un xxx_moc.cpp. Appelé automatiquement par qmake. Si oublié, génère message d'erreur :
undefined reference to finddialog.o:
In function `FindDialog::tr(char const*, char const*)':
/usr/lib/qt/src/corelib/global/qglobal.h:1430:
undefined reference to `FindDialog::staticMetaObject'
Visual C++' :
finddialog.obj : error LNK2001:
unresolved external symbol "public:~virtual int __thiscall
MyClass::qt_metacall(enum QMetaObject::Call,int,void * *)"
Permet de connecter des objets entre eux sans avoir besoin que les objets se connaissent mutuellement (découplage)
Nécessite la macro Q_OBJECT
mots clés : signals, private/protected/public slots, Q_SIGNALS, Q_SLOTS, SIGNAL(), SLOT()
Utilisé par pré-process (programme moc) pour générer le code (.moc)
Slot : identique à fonctions membres classiques (surchargable, peut être virtuelle, private protected ou public, peut être appelée)
SIGNAL et SLOT ne font en gros que convertir les paramètres en chaînes de caractères
Général :
connect(sender, SIGNAL(signal), receiver, SLOT(slot));
Qobject::connect(
button, SIGNAL(clicked()),
&app, SLOT(quit()));
Avec values et entre plusieurs widgets :
Qobject::connect(
spinBox, SIGNAL(valueChanged(int)),
slider, SLOT(setValue(int)));
Qobject::connect(
slider, SIGNAL(valueChanged(int)),
pinBox, SLOT(setValue(int)));
Connections :
Déconnexion possible mais en général pas utile (automatique par Qt quand les objets sont détruits) :
disconnect(lcd, SIGNAL(overflow()), this, SLOT(handleMathError()));
Paramètres :
Les signaux et slots doivent avoir les mêmes types de paramètres dans le même ordre :
connect(
ftp, SIGNAL(rawCommandReply(int, const QString &)),
this, SLOT(processReply(int, const QString &)));
Si le signal a des paramètres supplémentaires, ils sont ignorés :
connect(
ftp, SIGNAL(rawCommandReply(int, const QString &)),
this, SLOT(checkErrorCode(int)));
Si problème de connexion, émission d'un warning en runtime debug
Emettre un signal :
emit nom_du_signal(params) ;
Ajout de fonction déclarées avec signals (ou Q_SIGNALS) et private/protected/public slots (ou Q_SLOTS)
Plusieurs modules (= librairies). Les plus importantes : QtCore, QtGui, QtNetwork, QtOpenGL, QtScript, QtSql, QtSvg, and QtXml.
Inclusion d'un objet en spécifiant le module ou non ou tout le module :
#include <QLabel>
#include <QtGui/QLabel>
#include <QtGui>
Lancement de l'event loop : app.exec(); = en attente d'un event utilisateur, réponse du programme à l'event
Lors de l'appel de QApplication::exec(), nous lançons la boucle d'évènement de Qt. Qt émet quelques événements au démarrage (affichage et dessin des widgets). Ensuite, la boucle d'événements tourne en permanence pour capturer les events et les envoyer aux QObject.
event() capture tous les events puis dispatch aux différentes fonctions spécialisées :
Lors de traitement long, la boucle d'event ne tourne plus et il faut lancer manuellement le traitement des events :
qApp->processEvents();
qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
Avec une QProgressDialog :
QProgressDialog progress(this);
progress.setRange(0, RowCount);
progress.setModal(true);
for (int row = 0; row < RowCount; ++row) {
progress.setValue(row);
qApp->processEvents();
if (progress.wasCanceled()) {
return false;
}
}
Autres solutions :
void Spreadsheet::timerEvent(QTimerEvent *event)
{
if (event->timerId() == myTimerId) {
while (step < MaxStep && !qApp->hasPendingEvents()) {
performStep(step);
++step;
}
} else {
QTableWidget::timerEvent(event);
}
}
Event passe pas une boucle de gestion d'events. Les signaux-slots sont connectés directement ou en passant par la boucle de gestion des events.
Signaux : pour utiliser un widget ; event : pour implémenter un widget
Events dérivent de QEvent :
Qevent::type() retourne QEvent::MouseButtonPress
Pour modifier le comportement d'un event, il suffit de dériver la classe et surcharger la fonction.
void QWIdget::paintEvent(QPaintEvent *event)
void QWIdget::mousePressEvent(QMouseEvent *event)
Teste si le bouton est un LeftButton :
if (event->button() == Qt::LeftButton) {
Test si au moins le LeftButton est appuyé :
if (event->buttons() & Qt::LeftButton) {
void CodeEditor::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Qt::Key_Home:
if (event->modifiers() & Qt::ControlModifier) {}
break;
case Qt::Key_End:
...
default:
QWidget::keyPressEvent(event);
}
}
Pour Tab et Shift+Tab, event() n'envoit pas à keyPressEvent :
bool CodeEditor::event(QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast
(event);
if (keyEvent->key() == Qt::Key_Tab) {
return true;
}
}
return QWidget::event(event);
}
Testé si un rectangle est concerné par un event de dessin :
if (!event->region().intersect(rect).isEmpty()) {}
Il est possible d'utiliser 5 niveaux de filtres :
En 2 étapes :
CustomerInfoDialog::CustomerInfoDialog(QWidget *parent) : QDialog(parent)
{
firstNameEdit->installEventFilter(this);
lastNameEdit->installEventFilter(this);
}
bool CustomerInfoDialog::eventFilter(QObject *target, QEvent *event)
{
if (target == firstNameEdit || target == lastNameEdit
|| target == cityEdit || target == phoneNumberEdit) {
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast
(event);
if (keyEvent->key() == Qt::Key_Space) {
focusNextChild();
return true;
}
}
}
return QDialog::eventFilter(target, event);
}
Un event peut être accepté (et donc appliqué) ou rejeté :
void MainWindow::closeEvent(QCloseEvent *event)
{
if (okToContinue()) {
event->accept();
} else {
event->ignore();
}
}
PostEvent() // mis en file d'attente
SendEvent() // envoyé immédiatement
QEvent::User
Redessiner automatiquement l'arrière plan avec la couleur Dark :
setBackgroundRole(QPalette::Dark);
setAutoFillBackground(true);
Surcharger l'event de dessin.
void IconEditor::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.drawLine(x1, y1, x2, y2);
painter.fillRect(rect, color);
}
Utilisation des palettes de couleurs. Plusieurs groupes :
QWidget ::palette()
Couleur d'arrière-plan :
painter.setPen(palette().foreground().color());
Dessiner le rectangle de focus :
if (hasFocus()) {
QStyleOptionFocusRect option;
option.initFrom(this);
option.backgroundColor = palette().dark().color();
painter.drawPrimitive(QStyle::PE_FrameFocusRect, option);
}
ou
style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter, this);
ou utiliser un QStylePainter
QImage // pour I/O et mnaipulation de pixels
QPixmap // affichage à l'écran
QBitmap // depth = 1
QPicture // pour QPainter
QImageReader::supportedImageFormats()
QImageWriter::supportedImageFormats()
Dériver QMainWindow. Mettre :
Plusieurs zones dans une QMainWindow :
Document est modifié ou non :
setWindowModified(true);
Modifier l'icône de la fenêtre :
setWindowIcon(QIcon(":/images/icon.png"));
Fenêtres multiples. Delete automatique lorsque l'on ferme la fenêtre :
setAttribute(Qt::WA_DeleteOnClose);
Pour avoir la liste de toutes les fenêtres :
foreach (QWidget *win, QApplication::topLevelWidgets()) {
if (MainWindow *mainWin = qobject_cast
(win))
mainWin->updateRecentFileActions();
}
SDI : 1 document par fenêter ; MDI : plusieurs documents par fenêtre.
Affichage d'une page d'accueil avec QSplashScreen:
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QSplashScreen *splash = new QSplashScreen;
splash->setPixmap(QPixmap(":/images/splash.png"));
splash->show();
splash->showMessage("Message..."), Qt::AlignRight, Qt::white);
splash->finish(&mainWin);
delete splash;
return app.exec();
}
Changer le curseur :
QApplication::setOverrideCursor(Qt::WaitCursor);
QApplication::restoreOverrideCursor();
Clipboard :
QApplication::clipboard()->setText(str);
QString str = QApplication::clipboard()->text();
autres types : image, pixmap, mimeData
Utilise le concept d'action. Pour créer un menu :
Utilisation de & pour indiqué un raccourci clavier (si possible)
Pour associer un menu à une action :
fileMenu = menuBar()->addMenu(tr("&File"));
fileMenu->addAction(newAction);
Séparateurs entre 2 items de menu (barre horizontale) ou entre 2 menus (place les menus après le séparateur à droite de la barre de menu ; uniquement pour les styles CDE et Motif) :
separatorAction = fileMenu->addSeparator();
menuBar()->addSeparator();
Liste de fichiers récemment ouverts :
enum { MaxRecentFiles = 5 }; // taille max
QAction* recentFileActions[MaxRecentFiles]; // tableau d'actions
QAction* separatorAction; // séparateur
Création du menu :
for (int i = 0; i < MaxRecentFiles; ++i) {
recentFileActions[i] = new QAction(this);
recentFileActions[i]->setVisible(false);
connect(
recentFileActions[i], SIGNAL(triggered()),
this, SLOT(openRecentFile()));
}
Supprimer les fichiers inexistant de la liste :
QMutableStringListIterator i(recentFiles);
while (i.hasNext()) {
if (!QFile::exists(i.next()))
i.remove();
}
Mettre à jour le menu :
for (int j = 0; j < MaxRecentFiles; ++j) {
if (j < recentFiles.count()) {
QString text = tr("&%1 %2")
.arg(j + 1)
.arg(strippedName(recentFiles[j]));
recentFileActions[j]->setText(text);
recentFileActions[j]->setData(recentFiles[j]); // conserve le nom
// du fichier
recentFileActions[j]->setVisible(true); // masque si
// nécessaire
} else {
recentFileActions[j]->setVisible(false);
}
}
separatorAction->setVisible(!recentFiles.isEmpty()); // masque si
// nécessaire
Pour ouvrir le fichier :
QAction* action = qobject_cast
(sender());
if (action)
loadFile(action->data().toString());
Créer une barre d'outils :
fileToolBar = addToolBar(tr("&File"));
fileToolBar->addAction(saveAction);
fileToolBar->addSeparator();
Créer une barre de statuts. Peut contenir plusieurs widgets :
statusBar()->addWidget(locationLabel); // stretch = 0
statusBar()->addWidget(formulaLabel, 1); // stretch = 1
Permet de rassembler dans une seule classe une même fonctionnalité, appelée depuis un menu ou une barre d'outils.
Créer une action :
newAction = new QAction(tr("&New"), this);
newAction->setIcon(QIcon(":/images/new.png"));
newAction->setShortcut(QKeySequence::New);
newAction->setStatusTip(tr("Create a new spreadsheet file"));
connect(newAction, SIGNAL(triggered()), this, SLOT(newFile()));
Raccourci clavier : Ctrl + N pour créer un nouveau par exemple
newAction->setShortcut(QKeySequence::New);
exitAction->setShortcut(tr("Ctrl+Q"));
Peut être cheakable :
showGridAction->setCheckable(true);
showGridAction->setChecked(spreadsheet->showGrid());
Pour créer un menu contextuel dans un QWidget :
spreadsheet->addAction(cutAction);
spreadsheet->addAction(copyAction);
spreadsheet->addAction(pasteAction);
spreadsheet->setContextMenuPolicy(Qt::ActionsContextMenu);
Autre méthode : surcharger QWidget::contextMenuEvent(), créer un QMenu et appeler exec() dessus.
QVariant : permet de stocker différents types de données. Par exemple avec recentFile :
recentFileActions[j]->setData(recentFiles[j]);
loadFile(action->data().toString());
Plateforme dépendant par défaut : registres sur windows, fichier texte sous linux, les Core Foundation Preferences API sous Mac OS X.
Pour créer un settings :
QSettings settings("Organisation name", "Application name");
settings.setValue("geometry", saveGeometry());
settings.beginGroup("findDialog");
settings.setValue("matchCase", caseCheckBox->isChecked());
settings.endGroup();
Pour les lire :
QSettings settings("Software Inc.", "Spreadsheet");
recentFiles = settings.value("recentFiles").toStringList();
QSettings::QSettings(const QString & fileName, Format format, QObject * parent = 0)
QSettings::NativeFormat ou QSettings::IniFormat
qDebug() << arg; // nécessite #include
qDebug("...", arg);
qDebug() // QT_NO_DEBUG_OUTPUT
qWarning() // QT_NO_WARNING_OUTPUT QT_FATAL_WARNINGS
qFatal()
qCritical()
qInstallMsgHandler :
void myMessageOutput(QtMsgType type, const char *msg)
{
switch (type) {
case QtDebugMsg:
fprintf(stderr, "Debug: %s\n", msg);
break;
}
}
qInstallMsgHandler(myMessageOutput);
QT_NO_DEBUG
Q_ASSERT(cond)
Q_ASSERT_X(cond, where, what)
Q_CHECK_PTR(ptr)
En général, il faut :
class HexSpinBox : public QSpinBox
{
Q_OBJECT
public:
HexSpinBox(QWidget *parent = 0);
};
Contenu statique : pas besoin de redessiné ce qui était déjà visible lors d'un redimenssionnement, seule la nouvelle partie visible est a dessiner :
setAttribute(Qt::WA_StaticContents);
Le widget accepte le focus par clic ou tab
setFocusPolicy(Qt::StrongFocus);
Utilisation de la macro Q_PROPERTY pour déclarer des variables et ses accesseurs, permet de rendre visible les propriétés dans Qt Designer :
class IconEditor : public QWidget
{
Q_OBJECT
Q_PROPERTY(QColor penColor READ penColor WRITE setPenColor)
public:
void setPenColor(const QColor &newColor);
QColor penColor() const { return curColor; }
};
Taille préférée :
QSize QWidget::sizeHint() const;
Définie la politique pour la taille en fonction de sizeHint :
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
choix possibles :
Fixed // taille fixée à sizeHint
Minimum // sizeHint est un minimum
Maximum // sizeHint est un maximum
Preferred // sizeHint utilisé de préférence
Expanding // pas de préférence
Mettre à jour les tailles :
QWidget::updateGeometry();
Fixer la taille :
setFixedHeight(sizeHint().height());
QLabel de taille fixe :
locationLabel = new QLabel(" W999 ");
locationLabel->setAlignment(Qt::AlignHCenter);
locationLabel->setMinimumSize(locationLabel->sizeHint());
QVBoxLayout et QHBoxLayout, QGridLayout
Spacer → QSpacer ou addStretch();
QStackedLayout :
stackedLayout = new QStackedLayout;
QSplitter :
QSplitter splitter(Qt::Horizontal); // ou Qt::Vertical
pour sauvegarder les dimensions :
settings.setValue("mainSplitter", mainSplitter->saveState());
mainSplitter->restoreState(settings.value("mainSplitter").toByteArray());
QScrollArea :
QScrollArea scrollArea;
QDockWidget :
QDockWidget *shapesDockWidget = new QDockWidget(tr("Shapes"));
shapesDockWidget->setObjectName("shapesDockWidget");
shapesDockWidget->setWidget(treeWidget);
shapesDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
addDockWidget(Qt::RightDockWidgetArea, shapesDockWidget);
QToolBar :
QToolBar *fontToolBar = new QToolBar(tr("Font"));
fontToolBar->setObjectName("fontToolBar");
fontToolBar->addWidget(familyComboBox);
fontToolBar->addAction(underlineAction);
fontToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
addToolBar(fontToolBar);
QMdiArea:
mdiArea = new QMdiArea;
setCentralWidget(mdiArea);
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(spinBox);
layout->addWidget(slider);
window->setLayout(layout);
Dialogue = permet de proposer aux utilisateurs des options et des choix et mettre de les modifier.
Dialogues standards
Fonctionnalités prêtes à l'usage.
Création par appel de fonction statique :
int r = QMessageBox::warning(this, "Titre", "Message"),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
Test de la réponse :
if (r == QMessageBox::Yes) { }
if (r == QMessageBox::Cancel) { }
Autres dialogue d'information :
Dialogue pour choisir un nom de fichier à ouvrir :
QString fileName = QFileDialog::getOpenFileName(
this,
"Titre",
".", // répertoire par défaut
"Files (*.xls)"); // filtre pour les fichiers
Dialogue pour choisir un nom de fichier à enregistrer :
QString fileName = QFileDialog::getSaveFileName(
this,
"Titre",
".", // répertoire par défaut
"Files (*.xls)"); // filtre pour les fichiers
Dialogue « A propos » :
QMessageBox::about(this, "Titre", "Message"));
QWizardPage *createIntroPage()
{
QWizardPage *page = new QWizardPage;
page->setTitle("Introduction");
return page;
}
QWizard wizard;
wizard.addPage(createIntroPage());
wizard.show();
wizard()->setButtonText(QWizard::CustomButton1, tr("&Print"));
wizard()->setOption(QWizard::HaveCustomButton1, true);
connect(wizard(), SIGNAL(customButtonClicked(int)),
this, SLOT(printButtonClicked()));
Structure :
A title.
A subTitle.
A set of pixmaps, which may or may not be honored, depending on the wizard's style:
WatermarkPixmap (used by ClassicStyle and ModernStyle)
BannerPixmap (used by ModernStyle)
LogoPixmap (used by ClassicStyle and ModernStyle)
BackgroundPixmap (used by MacStyle)
registerField("className*", classNameLineEdit);
registerField("baseClass", baseClassLineEdit);
registerField("qobjectMacro", qobjectMacroCheckBox);
QString className = field("className").toString();
setPage(Page_Intro, new IntroPage);
setPage(Page_Evaluate, new EvaluatePage);
setPage(Page_Register, new RegisterPage);
setPage(Page_Details, new DetailsPage);
setPage(Page_Conclusion, new ConclusionPage);
int IntroPage::nextId() const
{
if (evaluateRadioButton->isChecked()) {
return LicenseWizard::Page_Evaluate;
} else {
return LicenseWizard::Page_Register;
}
}
hasVisitedPage()
setStartId().
Dialogues dérivent de QDialog (qui hérite de QWidget)
class FindDialog : public QDialog
{
Q_OBJECT
public:
FindDialog(QWidget *parent = 0);
signals:
void findNext(const QString &str, Qt::CaseSensitivity cs);
void findPrevious(const QString &str, Qt::CaseSensitivity cs);
private slots:
void findClicked();
void enableFindButton(const QString &text);
private:
QLabel *label;
QLineEdit *lineEdit;
QCheckBox *caseCheckBox;
QCheckBox *backwardCheckBox;
QPushButton *findButton;
QPushButton *closeButton;
};
Boutons :
Conteneurs page unique :
Conteneurs pages multiples :
Vues :
Widgets d'affichage :
Widgets d'édition :
Dialogue modal = reste en premier plan tan que affiché ; dialogue non modal : peut passer en second plan.
Pour afficher le dialogue non modal :
if (findDialog->isHidden()) {
findDialog->show();
} else {
findDialog->raise();
findDialog->activateWindow();
}
Pour un dialogue modal :
GoToCellDialog dialog(this);
if (dialog.exec()) {
QString str = dialog.lineEdit->text().toUpper();
}
La fonction exec() bloque le dialogue jusqu'à sa fermeture.
Connecter le bouton Ok au signal accept() et le bouton Cancel au signal reject() pour retourner QDialog::Accepted (true) ou QDialog::Rejected (false).
Dialogue changeant de forme :
Créer graphiquement les dialogues et mettre en place les signaux et slots.
Mettre à jour plus facilement une interface utilisateur : on n'a besoin de changer que le fichier ui, le reste du code est mis à jour automatiquement pas qmake
Génèrer un fichier xxx.ui (format xml décrivant l'interface) dans QtDesigner puis utiliser uic (user interface compiler) pour générer un fichier ui_xxx.h (déclaration des éléments de l'interface en C++) qui contient une classe Ui::xxx (propriété objectName du formulaire)
Accès aux widgets en fonction de leurs nom objectName
Ajout de la déclaration :
#include "ui_gotocelldialog.h"
Ouvertue du dialogue :
Ui::GoToCellDialog ui;
QDialog *dialog = new QDialog;
ui.setupUi(dialog);
dialog->show();
Ui:: GoToCellDialog contient la déclaration des widgets utilisés plus la fonction setupUi qui initialise ces widgets dans le QWidget passé en paramètre et connecte automatiquement les signaux signalName aux slots on_objectName_signalName()
connect(
lineEdit, SIGNAL(textChanged(const QString &)),
this, SLOT(on_lineEdit_textChanged()));
Ajout de ce qui manque par héritage :
#include "ui_gotocelldialog.h"
class GoToCellDialog : public QDialog, public Ui::GoToCellDialog
{
Q_OBJECT
public:
GoToCellDialog::GoToCellDialog(QWidget *parent) : QDialog(parent)
{
setupUi(this); // création des widgets
...
}
};
Utilisation de la promotion :
Limites : pas accès aux propriétés Q_PROPERTY dans Qt Designer et affiché comme l'objet avant promotion
Utilisation des plugins :
fichier .pro
TEMPLATE = lib
CONFIG += designer plugin release
HEADERS = ../iconeditor/iconeditor.h \
iconeditorplugin.h
SOURCES = ../iconeditor/iconeditor.cpp \
iconeditorplugin.cpp
RESOURCES = iconeditorplugin.qrc
DESTDIR = $$[QT_INSTALL_PLUGINS]/designer
fichier .h
#include <QDesignerCustomWidgetInterface>
class IconEditorPlugin : public QObject,
public QDesignerCustomWidgetInterface
{
Q_OBJECT
Q_INTERFACES(QDesignerCustomWidgetInterface)
public:
IconEditorPlugin(QObject *parent = 0) : QObject(parent) {}
QString name() const { return "IconEditor"; }
QString includeFile() const { return "iconeditor.h"; }
QString group() const { return tr("Image Manipulation Widgets"); }
QIcon icon() const { return QIcon(":/images/iconeditor.png"); }
QString toolTip() const { return tr("An icon editor widget"); }
QString whatsThis() const { return tr("Tooltips..."); }
bool isContainer() const { return false; }
QWidget *createWidget(QWidget *parent)
{ return new IconEditor(parent); }
};
Q_EXPORT_PLUGIN2(iconeditorplugin, IconEditorPlugin)
Dialogue dynamique. Chargement avec QUiLoader. Dans le .pro :
CONFIG += uitools
QUiLoader uiLoader;
QFile file("sortdialog.ui");
QWidget *sortDialog = uiLoader.load(&file);
Accès aux widgets du dialogue :
QComboBox* primaryColumnCombo =
sortDialog->findChild
("primaryColumnCombo");
if (primaryColumnCombo) {
...
}
ou avec la fonction globale qFindChild
Hum...
Création :
QString str = "User: ";
QString str("User: ");
Concaténation. Avec l'opérateur += :
str += userName + "\n";
Avec la fonction append() :
str = "User: ";
str.append(userName);
str.append("\n");
Avec la fonction sprintf, syntaxe identique à std::sprintf :
str.sprintf("%s %.1f%%", "perfect competition", 100.0);
Avec la fonction arg() (method chaining)
str = QString("%1 %2 (%3s-%4s)")
.arg("permissive").arg("society").arg(1950).arg(1970);
Extraction de sous chaîne :
QString str = "polluter pays principle";
qDebug() << str.mid(9, 4); // affiche "pays"
qDebug() << str.mid(9); // affiche "pays principle"
qDebug() << str.left(8); // affiche "polluter"
qDebug() << str.right(9); // affiche "principle"
Remplacement :
QString str = "a cloudy day";
str.replace(2, 6, "sunny"); // "a sunny day";
str.remove(2, 6); // "a day";
str.insert(2, "sunny"); // "a sunny day";
str.replace("&", "&");
QString str = " BOB \t THE \nDOG \n";
qDebug() << str.trimmed(); // "BOB \t THE \nDOG \n"
qDebug() << str.simplified(); // "BOB THE DOG"
Split() :
QString str = "polluter pays principle";
QStringList words = str.split(" "); // "polluter", "pays" et "principle"
words.sort();
str = words.join("\n"); // "pays\npolluter\nprinciple"
Localiser une sous-chaîne. Retourne -1 en cas d'erreur :
QString str = "the middle bit";
int i = str.indexOf("middle"); // retourne 4
url.startsWith("http:") // teste le début
url.endsWith(".png")) // teste la fin
Comparaison :
if (fileName == "readme.txt") {}
localeAwareCompare() pour comparer après traduction
toUpper()
toLower()
Test si vide :
str.isEmpty()
str.length() == 0
Fonction arg() permet de remplacer des placeholders par des variables (technique de chaînage) :
QString("%1 - %2").arg(intValue).arg(stringValue);
Convertir en entier :
str.mid(1).toInt()
str[0].unicode() - 'A'
Convertir nombre en QString :
QString::number(10);
QString::number(10, 16); // convesion hexadécimale
Conversion en nombre :
bool ok;
return text.toInt(&ok, 16); // hexadécimal
Conversion en char* :
str += " (1870)"; // automatique
QString::fromAscii()
QString::fromLatin1()
str.toAscii().data() // str.toAscii() retourne un QByteArray
str.toAscii().constData () // idem macro qPrintable()
str.toLatin1 ().data()
str.toLatin1 ().constData()
QByteArray, similaire à QString, avec des fonctions left(), right(), mid(), toLower(), toUpper(), trimmed() et simplified(). Stockage de données brutes 8 bits.
Prise en charge de l'internationnal :
Utilisation de tr(), lupdate et ltranslate
str[0] = 'A';
str[0] = QChar(0x41);
str[0] = 'Ñ';
str[0] = QChar(0xD1);
str[0] = QChar(0x03A3); // lettre grec sigma
str[0] = QChar(0x20AC); // €
Pour entrer du texte dans une autre langue :
QTextCodec::setCodecForTr(QTextCodec::codecForName("EUC-JP"));
QChar :
isPrint(), isPunct(), isSpace(), isMark(), isLetter(), isNumber(), isLetterOrNumber(), isDigit(), isSymbol(), isLower() et isUpper()
Pour la traduction automatique :
TRANSLATIONS = spreadsheet_de.ts spreadsheet_fr.ts
QObject::tr(sourceText, comment)
QCoreApplication::translate(Context, sourceText, comment)
lrelease -verbose spreadsheet.pro
QTranslator appTranslator;
appTranslator.load("myapp_" + QLocale::system().name(), qmPath);
app.installTranslator(&appTranslator);
QLocal permet de récupérer les préférences utilisateurs pour l'encodage :
fr // français
fr_CA // français canadien
fr_CA.ISO8859-15 // français canadian avec ISO 8859-15
Pour les classes ne possédant pas tr(), utiliser la macro :
Q_DECLARE_TR_FUNCTIONS()
Avec des arguments :
// WRONG
statusBar()->showMessage(tr("Host " + hostName + " found"));
// OK
statusBar()->showMessage(tr("Host %1 found").arg(hostName));
Avec QT_TR_NOOP macro ou QT_TRANSLATE_NOOP() avec contexte :
static const char * const flowers[] = {
QT_TR_NOOP("Medium Stem Pink Roses"),
QT_TR_NOOP("One Dozen Boxed Roses"),
QT_TR_NOOP("Calypso Orchid"),
QT_TR_NOOP("Dried Red Rose Bouquet"),
QT_TR_NOOP("Mixed Peonies Bouquet"),
0
};
for (int i = 0; flowers[i]; ++i)
comboBox->addItem(tr(flowers[i]));
Forcer l'utilisation de tr() ou QLatin1String() pour éviter les oublis :
DEFINES += QT_NO_CAST_FROM_ASCII
Pour les langues qui se lisent de droite à gauche (mais le fichier de traduction peut contenir un marqueur LTR qui fait la même chose automatiquement) :
QApplication::setLayoutDirection(Qt::RightToLeft)
QApplication::isRightToLeft()
Egalement possible de mettre les fichiers de traductions dans les ressources :
translations/myapp_de.qm
translations/myapp_fr.qm
translations/myapp_zh.qm
translations/qt_de.qm
translations/qt_fr.qm
translations/qt_zh.qm
RESOURCES += myapp.qrc
QString qmPath = ":/translations" ;
Le fichier ressource peut contenir différentes lanagues :
italic.png
cursivo.png
kursiv.png
Fonctions utiles :
QString::localeAwareCompare() // pour comparer les chaînes après traduction
toString() // pour convertir en chaîne
setScheme(), setUserName(), setPassword(), setHost(), setPort(), setPath(), setEncodedQuery() and setFragment(
1 lettre majuscule ou minuscule + 1 chiffre enter 1 et 9 + 0 à 2 chiffres entre 0 et 9 :
[A-Za-z][1-9][0-9]{0,2}
Validation hexadécimal (entre 0x00 et 0xFF) :
[0-9A-Fa-f]{1,8}
QIntValidator, QDoubleValidator et QRegExpValidator
Validation hexadécimal (entre 0x00 et 0xFF) :
QRegExpValidator *validator;
validator = new QRegExpValidator(QRegExp("[0-9A-Fa-f]{1,8}"), this);
Résulat retourné :
Invalid : le texte ne correspond pas
Intermediate : peut être une partie d'un texte acceptable
Acceptable : le texte correspond
QStringList wordList;
wordList << "alpha" << "omega" << "omicron" << "zeta";
QLineEdit *lineEdit = new QLineEdit(this);
QCompleter *completer = new QCompleter(wordList, this);
completer->setCaseSensitivity(Qt::CaseInsensitive);
lineEdit->setCompleter(completer);
QCompleter *completer = new QCompleter(this);
completer->setModel(new QDirModel(completer));
lineEdit->setCompleter(completer);
Pour ajouter des images dans une application :
Ajotuer un fichier de ressources dans le .pro :
RESOURCES = spreadsheet.qrc
Le fichier de ressources est un simple fichier XML (qui peut être créé automatiquement par Qt Creator)
images/icon.png
...
images/gotocell.png
Pour les utiliser :
QImage(":/images/icon.png");
findButton->setToolTip(tr("Find next"));
newAction->setStatusTip(tr("Create a new document"));
dialog->setWhatsThis(tr("
"
" The meaning of the Source field depends "
"on the Type field:"
"
"
"
Books have a Publisher"
"
Articles have a Journal name with "
"volume and issue number"
"
Theses have an Institution name "
"and a Department name"
"
"));
void MainWindow::help()
{
QUrl url(directoryOf("doc").absoluteFilePath("index.html"));
url.setScheme("file");
url.setFragment("editing");
QDesktopServices::openUrl(url);
}
CONFIG += assistant
QAssistantClient* assistant = 0;
if (!assistant)
QAssistantClient* assistant = new QAssistantClient("");
assistant->showPage(path);
Avantages :
Iterateurs Java plus facilement utilisable et iterateurs STL plus puissants et compatibles avec les algorithmes de la STL. 2 types à chaque fois : mutable ou non.
Itérateurs Java :
QVectorIterator
, QLinkedListIterator
et QListIterator
QMutableVectorIterator
, QMutableLinkedListIterator
et
QMutableListIterator
Modifications :
i.remove();
i.setValue(10);
i.insert(10);
Iterateurs STL
C
::iterator
C
::const_iterator
Pour éviter la copie implicite, utiliser de préférence at(), constBegin(), constEdn() et const_iterator
const T & at ( int i ) const
iterator begin ()
const_iterator begin () const
const_iterator constBegin ()
iterator end ()
const_iterator end () const
const_iterator constEnd () const
T & operator[] ( int i )
const T & operator[] ( int i ) const
Conteneurs séquentiels : QVector, QList, QLinkedList ; conteneurs associatifs : QMap, QHash
QVector
vect(3);
vect[0] = 1.0;
vect.append(1.0);
vect << 1.0 << 0.540302 << -0.416147;
QLinkedList
list;
list.append("Clash");
QLinkedList
::iterator i = list.find("Ramones");
list.insert(i, "Tote Hosen");
QMap
QMap
map;
map.insert("eins", 1);
map["eins"] = 1;
int val = map.value("dreiundzwanzig");
keys() : retourne la QList des clés, values() retourne la QList des valeurs.
Clés multiples : avec insertMulti() ou QMultiMap :
QMultiMap
multiMap;
multiMap.insert(1, "one");
QList
vals = multiMap.values(1);
QHash
Clés multiples : avec insertMulti() ou QMultiHash.
QCache
Autres : QPair
QList :
T doit être :
QList
> list;
Pour la clé, il faut en plus que soit définit l'opérateur <
Itérateur Java :
QList
list;
QListIterator
i(list);
while (i.hasNext()) {
do_something(i.next());
}
Reverse :
QListIterator
i(list);
i.toBack();
while (i.hasPrevious()) {
do_something(i.previous());
}
Itérateurs STL :
QList
::iterator i = list.begin();
while (i != list.end()) {
*i = qAbs(*i);
++i;
}
Mutable : permet de modifier les valeurs ; Standard : ne le permet pas
Conteneurs séquentiels :
QLinkedList
list;
foreach (Movie movie, list) {
if (movie.title() == "Citizen Kane") {
std::cout << "Found Citizen Kane" << std::endl;
break;
}
}
Conteneurs associatifs :
QMultiMap
map;
...
foreach (QString key, map.keys()) {
foreach (int value, map.values(key)) {
do_something(key, value);
}
}
Algorihms : qSort(), qBinaryFind()
class SpreadsheetCompare
{
public:
bool operator()(const QStringList &row1,
const QStringList &row2) const;
enum { KeyCount = 3 };
int keys[KeyCount];
bool ascending[KeyCount];
};
QList
rows;
qStableSort(rows.begin(), rows.end(), compare);
Conteneur associatif :
QMapIterator
i(map);
while (i.hasNext()) {
i.next();
if (i.value() > largestValue) {
largestKey = i.key();
largestValue = i.value();
}
}
qFind(). Complexité linéaire :
QStringList list;
list << "Emma" << "Karl" << "James" << "Mariette";
QStringList::iterator j = qFind(list.begin(), list.end(), "Petra");
qBinaryFind() : idem que qFind() sur liste triée. Plus rapide que qFind()
qFill() :
QLinkedList
list(10);
qFill(list.begin(), list.end(), 1009);
qCopy() :
QVector
vect(list.count());
qCopy(list.begin(), list.end(), vect.begin());
qSort() :
qSort(list.begin(), list.end());
Utilise par défaut l'opérateur < (tri croissant). Pour tri décroissant :
qSort(list.begin(), list.end(), qGreater
());
Avec un opérateur défini par l'utilisateur :
bool insensitiveLessThan(const QString &str1, const QString &str2)
{
return str1.toLower() < str2.toLower();
}
qSort(list.begin(), list.end(), insensitiveLessThan);
qStableSort() : idem que qSort() mais garentie que les éléments égaux (selon le critère de tri) sont conservés dans le même ordre.
QDeleteAll() : appelle delete sur tous les éléments d'un conteneur :
qDeleteAll(list);
list.clear();
Autres :
qSwap()
qMax()
qMin()
qCopyBackward()
qEqual()
Utilisation de l'idom COW (copy-on-write), permet de passer un conteneur par copie sans surcoût de copie,
Lors d'un accès en lecture (fonctions const) : pas de copie, par exemple avec at() ou value()
Lors d'un accès en écriture (fonctions non const) : copie des données
Utilisation d'un compteur interne :
QString str1 = "Humpty"; // size1 = 1
QString str2 = str1; // pas de copie cachée // size1 = 2
str2[0] = 'D'; // copie lors de l'écriture // size1 = 1 ; size2 = 1
str2.truncate(4); // pas de copie // size1 = 1 ; size2 = 1
str1 = str2; // suppression de "Humpty" // size1 = 0 ; size2 = 2
Stockage de données différentes dans une même structure : par conversion en QString/QByteArray ou avec QVariant (plus type safe).
Structures complexes avec les map, mais peut efficace et lisible :
QMap
pearMap;
pearMap["Standard"] = 1.95;
pearMap["Organic"] = 2.25;
QMap
fruitMap;
fruitMap["Orange"] = 2.10;
fruitMap["Pineapple"] = 3.85;
fruitMap["Pear"] = pearMap;
Avec QtGui, besoin d'utiliser la fonction template :
QIcon icon("open.png");
QVariant variant = icon;
QIcon icon = variant.value
();
Types par défaut
Types utilisateurs :
Q_DECLARE_METATYPE(BusinessCard)
BusinessCard businessCard;
QVariant variant = QVariant::fromValue(businessCard);
...
if (variant.canConvert
()) {
BusinessCard card = variant.value
();
...
}
Pour MSVC6 : utiliser qVariantFromValue(), qVariantValue
Déclaration des opérateurs << et >> pour QDataStream :
qRegisterMetaTypeStreamOperators
("BusinessCard");
QIODevice : abstraction générale pour écrire et lire sur différentes sources (fichier, réseau, buffer, etc.). Classes dérivées : QFile (fichier), QTemporaryFile (fichier temporaire), QBuffer (tableau d'octets), QProcess (communication inter-process), QTcpSocket (réseau TCP), QUdpSocket (réseau UDP), QSslSocket (réseau chiffré). Les 3 premiers sont en accès aléatoire (on peut lire n'importe où) alors que les 4 autres sont séquentiels (on ne peut lire qu'une fois, en partant du premier octet et en lecture séquentielle).
Lecture directe avec peek() et ungetChar().
Permet d'ouvrir un fichier en lecture ou écriture, en mode texte ou binaire.
Ouverture en écriture :
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {
}
Ouverture en lecture :
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
}
Classes haut-niveau, qui permet de lire et écrire dans n'importe quelle classe dérivant de QIODevice, en étant indépendant de la plateforme (sérialisation : encodage, endianness). QDataStream permet de lire des données binaires et QTextStream des données textes.
Pour ouvrir un flux :
QDataStream out(&file);
out.setVersion(QDataStream::Qt_4_3); // permet de spécifier la version
écriture (avec cast d'un int dans un type entier de Qt) :
out << quint32(MagicNumber);
lecture :
in >> magic;
Pour les types entiers, caster vers un type safe :
qint8, quint8, qint16, quint16, qint32, quint32, qint64 ou quint64
Pour la gestion de la version, il est également possible de coder la version en début de message :
// écriture
QDataStream out(&file);
out << quint16(out.version());
// lecture
quint16 version;
QDataStream in(&file);
in >> version;
if (version > in.version()) {
std::cerr << "Version trop récente" << std::endl;
return false;
}
in.setVersion(streamVersion);
Encodage
Pour les types définis par l'utilisateur, possibilité de définir les opérateurs << et >> :
QDataStream &operator<< (QDataStream &out, const T& t)
{
out << (...);
return out;
}
QDataStream &operator>> (QDataStream &in, T& t)
{
QString x;
in >> x;
t = T(x);
return in;
}
En fournissant ces opérateurs, il est alors possible d'utiliser directement les conteneurs :
QList
tt ;
out << tt;
QList
tt;
in >> tt;
Il est possible d'utiliser également qRegisterMetaTypeStreamOperators
Qt fournit deux classes supplémentaires pour la gestion des fichiers : QDir pour gérer les répertoires, et QFileInfo pour obtenir des informations sur les fichiers.
Les types de bases (bool, float, double, etc.), de nombreuses classes de Qt (QString, QColor, etc.), les conteneurs de Qt (QVector, QList, etc.) sont utilisable directement avec QDataStream et QTextStream. Le type int n'est pas utilisable directement, puisque sont implémentation est dépendant de la plateforme. A la place, il faut utiliser l'un des types d'entitier de Qt : quint32 (« q » pour Qt, « u » pour unsigned, « 32 » pour la taille en bits).
Possibiltié de lire ou écrire la totalité d'un fichier avec QFile::write() et QFile::readAll().
Pour tester si une erreur est survenu : error() retourne QFile::NoError.
QTextStream par défaut utilise local 8-bits encoding (QTextCodec::codecForLocale())
QTextStream in(&file);
in.setCodec("UTF-16");
in.setGenerateByteOrderMark(true);
in.setCodec("ISO 8859-1");
setOrientation(QPrinter::Portrait ou QPrinter::Landscape)
setPaperSize(QPrinter::A4, QPrinter::Letter, QPrinter::Custom, etc.)
setResolution(int dpi)
setFullPage(bool)
setCopyCount(int)
setOutputFormat(QPrinter::NativeFormat, PdfFormat ou PostScriptFormat)
Exemple :
QPrinter printer;
printer.setOutputFormat(QPrinter::PdfFormat);
printer.setOutputFileName("/foobar/nonwritable.pdf");
QPainter painter;
if (!painter.begin(&printer)) { // failed to open file
qWarning("failed to open file, is it writable?");
return 1;
}
painter.drawText(10, 10, "Test");
if (! printer.newPage()) {
qWarning("failed in flushing page to disk, disk full?");
return 1;
}
painter.drawText(10, 10, "Test 2");
painter.end();