System zasobów biblioteki Qt.
W przypadku prawie każdego programu zachodzi potrzeba dostarczenia, wraz z plikiem wykonywalnym, dodatkowych zasobów.
Dobrym przykładem są obrazy wykorzystywane w graficznym interfejsie użytkownika. Możemy oczywiście dostarczać obrazy jako część instalatora, który rozpakuje je podczas instalacji do katalogu aplikacji. Ma to jednak swoje wady: jest dość uciążliwe i może nie wyglądać profesjonalnie.
Na szczęście w przypadku biblioteki Qt istnieje niezależna od platformy alternatywa – tzw. system zasobów. Pozwala on agregować i łąćzyć dodatkowe zasoby z plikiem wykonywalnym aplikacji. Skutkuje to co prawda zwiększoną wagą pliku wynikowego, ale równocześnie powoduje, że musimy dostarczyć użytkownikowi tylko jeden element – plik wykonywalny.
Jak to działa ?
Do projektu aplikacji możemy dołączyć pliki o rozszerzeniu *.qrc, które opisują zawartość zasobów – jest to po prostu lista ścieżek do plików, zapisana w formacie wykorzystującym składnię XML.
W tym miejscu powinien się pojawić dobry przykład. Trochę skrócimy sobie drogę i wykorzystamy kod, stworzony przy okazji trzeciej części tutoriala. Jeśli nie mieliście okazji przerobić tamtej części – po prostu to zróbcie 🙂
Wynikowo mamy więc projekt z jednym plikiem źródłowym (main.cpp) o następującej treści:
#include <QApplication> #include <QPushButton> #include <QMainWindow> #include <QLabel> int main(int argc, char *argv[]) { QApplication program(argc, argv); QMainWindow window; window.setGeometry(100, 100, 500, 400); window.setWindowTitle("Okno Główne"); QPushButton button("Zamknij", &window); button.setGeometry(200, 350, 100, 40); QLabel label("Tekst etykiety :)", &window); label.setGeometry(200, 150, 400, 50); QObject::connect(&button, SIGNAL(clicked()), &program, SLOT(quit())); window.show(); return program.exec(); }
Aby dodać plik zasobów do projektu, klikamy prawym przyciskiem myszy na nazwę projektu i wybieramy z listy Add New…->Qt->Qt Resource File i klikamy Choose…. Następnie podajemy nazwę pliku – np. resources – i klikamy na Next > oraz Finish.
Zanim dodamy zasoby do naszego pliku, musimy mieć coś ciekawego na dysku 🙂 Tworzymy folder images (w naszym folderze projektowym), do którego kopiujemy plik exit.png. Można go zapisać stąd:
Wracamy do pliku resources.qrc i w dolnej części QtCreator’a klikamy Add->Add Prefix. Jako nazwę prefixu proponuję ustawić jedynie jeden prawy ukośnik (/). Teraz możemy w tym samym miejscu wybrać Add->Add Files i wskazać pliki w naszym folderze images. Jak widzimy w górnej części okna, QtCreator dodał nasz jedyny plik do pliku zasobów.
W następnym kroku zapisujemy zmiany i wracamy do pliku main.cpp.
Kluczowa zmiana w main.cpp.
Na początku funkcji main dodajemy następującą linię:
Q_INIT_RESOURCE(resources);
Makro Q_INIT_RESOURCE za za zadanie włączenie zasobów do pliku wykonywalnego. Jako parametr podajemy nazwę pliku zasobów, bez rozszerzenia.
Teraz możemy skorzystać z dodanych do projektu zasobów – ustawimy ikonę dla przycisku Zamknij. Obiektowi klasy QPushButton można ustawić nie tylko tekst, ale również dowolną ikonę. Finalnie plik main.cpp wygląda tak:
#include <QApplication> #include <QPushButton> #include <QMainWindow> #include <QLabel> #include <QIcon> int main(int argc, char *argv[]) { Q_INIT_RESOURCE(resources); QApplication program(argc, argv); QMainWindow window; window.setGeometry(100, 100, 500, 400); window.setWindowTitle("Okno Główne"); QPushButton button("Zamknij", &window); button.setGeometry(200, 350, 100, 40); button.setIcon(QIcon(":/images/exit.png")); QLabel label("Tekst etykiety :)", &window); label.setGeometry(200, 150, 400, 50); QObject::connect(&button, SIGNAL(clicked()), &program, SLOT(quit())); window.show(); return program.exec(); }
Po utworzeniu przycisku i ustawieniu jego położenia oraz rozmiarów, ustawiamy dla niego ikonę za pomocą funkcji setIcon. Za pomocą tej funkcji możemy ustawiać ikony dla wszelkiego rodzaju okien, przycisków, umieszczać je na pasku narzędzi i w menu aplikacji. Funkcja setIcon przyjmuje jako argument obiekt klasy QIcon – tutaj jawnie tworzymy taki obiekt, jako parametr konstruktora podając „:/images/exit.png” – ścieżkę do pliku ikony.
Dwukropek jest tutaj kluczowy. Dwukropek na początku ścieżki mówi konstruktorowi klasy QIcon, że pliku ikony ma szukać w zasobach aplikacji, a nie na dysku.
Oto efekt naszego kodu po uruchomieniu aplikacji:
Jak widać, system zasobów w bibliotece Qt jest bardzo intuicyjny.
Resource Compiler
Należy w tym miejscu wspomnieć o narzędziu, które umożliwia nam całą tę operację, związaną z wygodnym dodawanie zasobów do pliku wykonywalnego. To narzędzie to RCC (Resource Compiler) – tzw. kompilator zasobów. Generuje on dodatkowe pliki źródłowe, zawierające zasoby wymienione w plikach *.qrc. Wewnętrznie wygląda to tak, że zasoby są zapisywane w postaci binarnej i dołączane do pliku wykonywalnego aplikacji. Oczywiście dla nas wszystko dzieje się w pełni automatycznie, a my widzimy tylko (lub aż) ostateczny rezultat.
Na koniec chciałbym jeszcze pokazać, jak wygląda treść naszego pliku resources.qrc (jest to de facto zwykły plik tekstowy):
<RCC> <qresource prefix="/"> <file>images/exit.png</file> </qresource> </RCC>
Jak widać, składnia takiego pliku jest dość prosta i można bez problemu edytować go w dowolnym edytorze tekstu. Ja polecam jednak korzystać z dobrodziejstw narzędzi które dostajemy – w tym przypadku mamy niezawodnego QtCreator’a, który integruje system zasobów w ramach swoich funkcjonalności 🙂