Portál AbcLinuxu, 30. dubna 2024 22:16

Grafické programy v Qt 4 – 7 (lokalizace a data programu)

29. 5. 2009 | David Watzke
Články - Grafické programy v Qt 4 – 7 (lokalizace a data programu)  

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.

Lokalizace (kalendář)

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.

Qt 4 kalendář

Data programu

Č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.

Qt 4 resources

Závěr

V příštím díle si mimo jiné povíme něco o stylování GUI pomocí CSS.

Seriál Qt 4 - psaní grafických programů (dílů: 12)

První díl: Grafické programy v Qt 4 - 1 (úvod, hello world), poslední díl: Grafické programy v Qt 4 – 12 (stylování GUI pomocí CSS).
Předchozí díl: Grafické programy v Qt 4 - 6 (WebKit, Phonon, taby, modálnost oken)
Následující díl: Grafické programy v Qt 4 – 8 (TCP klient)

Související články

Cmake: zjednoduš si život
Seriál: Kommander
Seriál: KDE: tipy a triky
Seriál: Začíname KProgramovať
Co přináší KDE 4 - (alfaverze, porty a D-BUS)
Co přináší KDE 4 - (technologie)
Novinky v KDE 4
Jaké je KDE 4.0.0
Seriál: KDE 4.1 - megarecenze

Odkazy a zdroje

doc.trolltech.com

Další články z této rubriky

LLVM a Clang – více než dobrá náhrada za GCC
Ze 4 s na 0,9 s – programovací jazyk Vala v praxi
Reverzujeme ovladače pro USB HID zařízení
Linux: systémové volání splice()
Programování v jazyce Vala - základní prvky jazyka

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.