Portál AbcLinuxu, 30. dubna 2024 22:16
V dnešním díle se dozvíte, jak program lokalizovat a jak k němu korektně přibalit nějaká data - třeba obrázky, ikony nebo zvuky.
Hned na začátku seriálu jsem doporučoval označovat všechny řetězce viditelné uživateli pro překlad, což se dělá vložením řetězce do metody tr()
třídy QObject
. Když budete chtít překladatelům trochu usnadnit práci, můžete do kódu přidat ještě speciální komentáře, které budou obsahovat například informace o kontextu, což se hodí především u rozsáhlých projektů. Tyto komentáře se vytvářejí tak, že přímo nad řádek s řetězcem označeným pro překlad vytvoříte C++ komentář začínající dvojtečkou:
//: this is the hidden button hBtn = new QPushButton(tr("Explode!")); /*: example string */ QString str(tr("Go"));
Tohle ovšem nestačí k tomu, aby program použil dodané překlady. Je třeba načíst překlad ve funkci main()
pomocí vytvoření a registrace objektu QTranslator
, což je vysvětleno o kousek níže, u zdrojového kódu main.cpp
. Z programátorského hlediska nám tyto znalosti většinu času postačí. Jak ale vytvořit překlad?
Je nutné přidat do projektového souboru (s příponou .pro
) následující:
# "cs" je ISO 639 kód pro češtinu, "CZ" je ISO 3166 kód pro ČR TRANSLATIONS = calendar_cs_CZ.ts
A v případě, že nechceme překlady ve výchozím kódování (Latin1), ale v UTF-8, tak ještě
CODECFORTR = UTF-8
Nyní, když spustíte program lupdate
s připraveným .pro souborem jako argument, vygenerují (a později už jen aktualizují) se vám soubory, které jste zadali v .pro
souboru do proměnné TRANSLATIONS
.
$ lupdate calendar.pro Updating 'calendar_cs_CZ.ts'... Found 1 source text(s) (1 new and 0 already existing)
Tímto nám vznikl soubor calendar_cs_CZ.ts
, což je jakési XML, které lze editovat pomocí grafického nástroje Qt Linguist. Spustíme si jej tedy a otevřeme v něm tento soubor (z příkazové řádky pomocí linguist calendar_cs_CZ.ts
). Zvolíme jazyk, do kterého překládáme, a můžeme se do toho pustit. V případě tohoto ukázkového programu není příliš co překládat, takže přeložíme "Add" jako "Přidat", uložíme změny a tím naše překladatelská práce hasne, od toho jsou tu přece jiní :-)
Posledním krokem je spuštění lrelease
(opět s .pro
souborem jako argument), což vygeneruje z .ts
souborů binární soubory s příponou .qm
, které pak program načítá.
$ lrelease calendar.pro Updating '/home/dave/abcl/qt4-7/calendar/calendar_cs_CZ.qm'... Generated 1 translation(s) (1 finished and 0 unfinished)
Teď, když spustíte program z adresáře, kde je soubor calendar_cs_CZ.qm
, tak za předpokladu, že máte systémový jazyk nastavený na češtinu, se vám program spustí v češtině.
Ukázkový program pro tento díl obsahuje kalendář (QCalendarWidget
) a umožňuje ke každému dni napsat libovolný počet poznámek. Poznámky se ukládají do konfiguračního souboru přes objekt QSettings
, jehož použití jsme si ukazovali již ve druhém díle.
main.cpp
: Zde je novinkou zavedení lokalizace. Vytvoříme si instanci objektu QTranslator
, jehož metodou load()
načteme soubor s překladem. Metodě předáváme název .qm
souboru bez přípony. Jako druhý argument můžeme předat cestu k adresáři, kde se má soubor hledat. Načtenou lokalizaci poté zavedeme do programu metodou QCoreApplication::installTranslator()
(nejjednoduššeji přes instanci QApplication
).
#include <QApplication> #include <QLocale> #include <QTextCodec> #include <QTranslator> #include "mainwindow.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); // nastavíme název programu a doménu pro QSettings app.setApplicationName("calendar"); app.setOrganizationDomain("watzke.cz"); // kódování pro lokalizaci bude UTF-8 QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8")); QTranslator t; /* načteme překlad podle aktuální locale ze souboru ve tvaru: * název programu + podtržítko + jazyk_země * kde jazyk je dvoumístný kód jazyka (ISO 639) malými písmeny a * země je dvoumístný kód země (ISO 3166) velkými písmeny, např. cs_CZ */ t.load( app.applicationName() + "_" + QLocale::system().name() ); // a přiřadíme jej k této instanci programu app.installTranslator(&t); MainWindow win; win.show(); return app.exec(); }
mainwidget.h
: API.
#ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QWidget> #include <QSettings> class QCalendarWidget; class QLineEdit; class QListWidget; class MainWidget : public QWidget { Q_OBJECT public: MainWidget(); private: QSettings settings; QCalendarWidget* calendar; QLineEdit* lineEdit; QListWidget* list; QString getDate(); private slots: void addItem(); void removeItem(); void listItems(); }; #endif // MAINWIDGET_H
mainwidget.cpp
: Zde je novinkou použití tříd QDate
a QDateTime
. Slouží pro manipulaci s datem, respektive datem a časem. Mají například statické metody pro získání aktuálního data a umí jej různě formátovat.
#include "mainwidget.h" #include <QAction> #include <QCalendarWidget> #include <QDateTime> #include <QHBoxLayout> #include <QLineEdit> #include <QListWidget> #include <QPushButton> #include <QVBoxLayout> MainWidget::MainWidget() { // vytvoříme kalendář calendar = new QCalendarWidget; // nastavíme první den v týdnu na pondělí calendar->setFirstDayOfWeek(Qt::Monday); // skryjeme vertikální hlavičku s čísly týdnů calendar->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader); // vytvoříme jednořádkový editor textu lineEdit = new QLineEdit; // a jednoduchý seznam, list = new QListWidget; // ve kterém lze vybrat v jednu chvíli jen jednu položku list->setSelectionMode(QAbstractItemView::SingleSelection); // a který má akci, jež se spustí při stisknutí klávesy Delete QAction* removeShortcut = new QAction(list); removeShortcut->setShortcut(QKeySequence("Delete")); list->addAction(removeShortcut); // tlačítko pro přidání položky do seznamu QPushButton* addBtn = new QPushButton(tr("Add")); QHBoxLayout* lineLayout = new QHBoxLayout; lineLayout->addWidget(lineEdit); lineLayout->addWidget(addBtn); // vytvoříme vertikální rozložení QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(calendar); layout->addLayout(lineLayout); layout->addWidget(list); // propojíme ovládací prvky connect(calendar, SIGNAL(selectionChanged()), this, SLOT(listItems())); connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(addItem())); connect(addBtn, SIGNAL(clicked()), this, SLOT(addItem())); connect(removeShortcut, SIGNAL(triggered()), this, SLOT(removeItem())); // načteme položky pro výchozí (dnešní) datum po spuštění programu listItems(); } // přidá položku se zadaným textem k vybranému datu void MainWidget::addItem() { // načteme text z textového pole QString text = lineEdit->text(); // a pokud je prázdný, tak končíme if(text.isEmpty()) return; // získáme aktuální datum QString date = getDate(); if(date.isEmpty()) return; // získáme aktuální datum s časem QDateTime datetime(QDateTime::currentDateTime()); // a přidáme jej na začátek řetězce text.prepend(datetime.toString("dd.MM.yy@hh.mm: ")); // index přidávané položky (v nastavení) QString index = "0"; // přepneme se v nastavení do sekce s dnešním datem settings.beginGroup(date); // pokud jsou zde již nějaké položky, nastavíme správný index (poslední + 1) if(settings.childKeys().size()) index = QString::number(settings.childKeys().last().toInt() + 1, 10); // uložíme položku do nastavení settings.setValue(index, text); // a nezapomeneme (!) vyskočit ze sekce s dnešním datem settings.endGroup(); // nakonec přidáme položku i do seznamu QListWidgetItem* item = new QListWidgetItem(text); list->addItem(item); list->setCurrentItem(item); // a vyprázdníme textové pole lineEdit->clear(); } // odstraní vybranou položku void MainWidget::removeItem() { //if(!list->count()) // return; // index vybrané položky v seznamu int row = list->currentRow(); // pokud nebyla vybrána žádná položka, tak končíme if(row < 0) return; QString date = getDate(); if(date.isEmpty()) return; settings.beginGroup(date); // index vybrané položky v nastavení QString key = settings.childKeys().at(row); // pokud odstraňujeme poslední položku, odstraníme i sekci daného data if(settings.childKeys().size() == 1) settings.remove(""); else settings.remove(key); settings.endGroup(); // nakonec odstraníme položku i ze seznamu delete list->takeItem(row); } // načte položky k danému dni z nastavení do seznamu void MainWidget::listItems() { // vyprázdníme seznam list->clear(); QString date = getDate(); if(date.isEmpty()) return; settings.beginGroup(date); // postupně načteme do seznamu všechny položky v sekci daného data foreach(QString key, settings.childKeys()) list->addItem(settings.value(key).toString()); settings.endGroup(); // a označíme list->setCurrentRow(0); } // vrátí aktuální datum jako řetězec v ISO formátu (yyyy-MM-dd) QString MainWidget::getDate() { QDate qdate = calendar->selectedDate(); if(qdate.isNull()) return QString(); return qdate.toString(Qt::ISODate); }
Zdrojáky si můžete stáhnout v archívu calendar.tar.bz2.
Často se stane, že je potřeba program dodávat s různými vlastními daty. Může jít například o ikony, obrázky nebo zvuky. Qt pro tuhle situaci má řešení, a to Resource System. Standardní přístup je ten, že se binární data, která program potřebuje, zakompilují do binárky programu. A jak to udělat?
Musíme si (v adresáři s programem) vytvořit .qrc
soubor, což je vlastně XML, ve kterém uvedeme všechny potřebné soubory. Máme dvě možnosti: Buď můžeme použít Qt Creator, v něm si do projektu přidat "resource file" a všechno si naklikat, nebo si tento soubor vytvořit ručně. Popíšu zde pouze ruční metodu, abych vysvětlil, co se vlastně ve skutečnosti děje. Náš ukázkový soubor resources.qrc
bude vypadat následovně:
<!DOCTYPE RCC><RCC version="1.0"> <qresource> <file>data/image.jpg</file> <file alias="abcicon.png">data/icon.png</file> </qresource> </RCC>
Do párového tagu file
po jednom vložíme cesty ke všem souborům. Cesty jsou relativní k umístění .qrc
souboru. Atribut alias umožňuje zadat alias, pod kterým bude daný soubor dostupný (viz níže).
Nyní o tomto souboru dáme vědět qmake
tím, že přidáme do .pro souboru tento řádek:
RESOURCES += resources.qrc
To je vše. K zakompilovaným souborům lze přistupovat zadáním relativní cesty za prefix :/
. Třeba takto:
QPixmap icon(":/abcicon.png");
main.cpp
: Ukázka načtení ikonky okna a obrázku. Program je tak jednoduchý, že můžeme udělat vše ve funkci main()
.
#include <QApplication> #include <QIcon> #include <QLabel> int main(int argc, char *argv[]) { QApplication a(argc, argv); QLabel w; // nastavíme ikonu okna (favicon abclinuxu) w.setWindowIcon(QIcon(":/abcicon.png")); // nastavíme bílé pozadí w.setPalette(QPalette(Qt::white)); w.setAutoFillBackground(true); // načteme průhledný gif (logo abclinuxu) w.setPixmap(QPixmap(":/data/image.gif")); w.show(); return a.exec(); }
Zdrojáky si můžete stáhnout v archívu resources.tar.bz2.
V příštím díle si mimo jiné povíme něco o stylování GUI pomocí CSS.
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.