2004 08 Katalog filmów [Programowanie]


dla programistów
Katalog filmów
Marek Sawerwain
czerwcowym numerze
Listing 1. Klasa aplikacji
Linux+ opisałem prostą
#include "wx/wx.h"
aplikację napisaną w ję-
class hw_app : public wxApp {
Wzyku C#. Była ona nie-
public:
skomplikowana, ale jedną z jej zalet była
virtual bool OnInit();
możliwość bardzo łatwego przenoszenia
};
kodu pomiędzy Linuksem a Windows. DECLARE_APP(hw_app)
Jeśli musimy napisać program, który
będzie łatwy do przeniesienia pomię- jakiś mechanizm ich rozpoznawania, aby
dzy różnymi systemami, a C# czy Java z sprawdzić, czy taki film już mamy. Tutaj
jakichś powodów nie jest dla nas odpo- znakomicie sprawdzają się tzw. odciski
wiednim wyborem, pozostaje nam C/C++ (ang. hash). Istnieje wiele różnych algoryt-
oraz wybór odpowiedniej biblioteki GUI. mów, które je generują, a my wybierzemy
Bardzo dobrym narzędziem, sprawdzają- system, który jest stosowany w progra-
cym się znakomicie w takich zadaniach, mach typu eDonkey.
jest biblioteka o nazwie wxWidgets (kiedyś Ostatnia sprawą, którą chciałbym po-
ta biblioteka nazywała się wxWindows, ale ruszyć, to ogólny schemat naszej aplikacji.
pewnej firmie się to nie spodobało i auto- Tutaj, jak zawsze, najlepiej jest przygoto-
rzy byli zmuszeni zmienić nazwę). wać odpowiedni schemat, np. taki jak na
Nowe rozwiązania najlepiej pozna- Rysunku 1. Na schemacie są cztery główne
wać na przykładach, więc napiszemy okna (oznaczone kolorem żółtym). Mamy
katalog filmów. okno główne, gdzie można utworzyć bazę
danych bądz przejść do trzech pozosta-
Założenia programu łych okien: okna dodawania nowego
Jak zawsze, zanim rozpocznie się pisanie filmu, okna wyszukiwania oraz okna
programu, warto poświęcić kilka chwil przeglądania i edycji danych. Kolorem nie-
na zastanowienie się, co dany program bieskim zostały oznaczone najważniejsze
ma robić. operacje na bazie danych. Wszystkie
Nasza aplikacja to baza danych. operacje na danych najlepiej zgroma-
CD/DVD
Po uruchomieniu dystrybucji Musimy wybrać odpowiedni system dzić w jednej klasie. Znacząco ułatwi
Linux+ Live CD/DVD można
baz danych. Podobnie jak przy bazie to napisanie nam programu. Dodatkowo,
przetestować działanie
teleadresowej z numeru czerwcowego, do tej klasy dodamy również takie opera-
omawianego programu.
będzie to system Sqlite, który przy tego cje, jak wyznaczenie podpisu oraz wycią-
typu niewielkich bazach danych spraw- gnięcie pojedynczej klatki.
Na płycie CD/DVD
dza się znakomicie. Tym razem będziemy
Na płycie CD/DVD znajdują
korzystać bezpośrednio z API Sqlite. Wyświetlamy pewien
się pliki zródłowe potrzebnych
Zastanówmy się, co możemy przecho- słynny komunikat
bibliotek, kompletny kod zródłowy
wywać w naszej bazie. Oprócz typowych Programowanie w wxWidgets jest dość
omawianego programu, gotowy
danych, takich jak tytuł filmu oraz imię i podobne do innych tego typu rozwią-
program wykonywalny
oraz listingi z artykułu. nazwisko reżysera, możemy postarać się zań, takich jak Qt, a nawet GTK+. Pod
o zapisanie w bazie jednej klatki z filmu. pewnymi względami jest nawet bardziej
O autorze
Oczywiście, oznacza to, że nasze filmy wygodne, np. podłączenie funkcji do zda-
Autor zajmuje się tworzeniem
muszą być w postaci plików avi, mpg i rzenia nie wymaga stosowania oddziel-
oprogramowania dla WIN32
podobnych. Wyciągniecie klatki to dość nego programu, tak jak w przypadku Qt.
i Linuksa. Zainteresowania: teoria
trudny problem, więc będziemy korzystać Zanim zaczniemy pisać naszą aplikację, na
języków programowania oraz
z oddzielnej biblioteki, a będzie nią Xine. początek podam niewielki przykład.
dobra literatura. Kontakt
z autorem: autorzy@linux.com.pl Skoro w naszej bazie katalogujemy Napiszemy nieskomplikowany pro-
filmy z plików, to warto byłoby dodać gram, który wyświetla komunikat
70
sierpień 2004
baza danych filmów dla programistów
Instalacja biblioteki
wxWidgets
Biblioteka wxWidgets w przypadku Linuk-
sa może być skompilowana na trzy różne
sposoby (a nawet na pięć, gdyż możemy
wykorzystać nadal stosowaną w środo-
wiskach uniksowych bibliotekę Motif lub
pakiet Wine).
Pierwszy sposób dotyczy pakietu
wxX11. Po kompilacji, która jak zwykle
sprowadza się do poleceń:
./configure  prefix=/opt
make
make install
Rysunek 1. Główne zdarzenia, które pojawiają się naszej aplikacji
otrzymamy pakiet, który wymaga tylko
i wyłącznie środowiska X do swojej pracy.
zawiera tylko jedną metodę: OnInit RE_CLASS definiujemy stałe elementy
Stanowi to zaletę tej odmiany wxWidgets.
 Listing 1 zawiera jej definicję. Podobnie klasy. Pojawia się również drugie makro:
Drugim sposobem jest wykorzysta-
nie biblioteki GTK. Co ważne, należy postąpimy w przypadku bazy danych DECLARE_EVENT_TABLE(). Jest ono odpo-
mieć zainstalowaną starszą wersję GTK
z filmami. wiedzialne za definicję tabeli zdarzeń,
1.2.x. W naszej filmowej bazie danych
Treść Listingu 1 wyróżnia się tylko w której połączymy zdarzenia z metodami
korzystamy z pakietu wxGTK. Kompilację
jednym dodatkowym makrem: DECLA- klasy. Nasz pierwszy programik ma tylko
przeprowadzamy podobnie jak powyżej,
RE_APP. Implementuje ono stałe elementy, jedno zdarzenie, które jest generowane
ale skrypt configure wywołujemy z nastę-
a mianowicie funkcję wxGetApp. Ważniej- w momencie kliknięcia na przycisk.
pującymi opcjami:
sze czynności wykonujemy w składowej Z istotnych elementów, o których trzeba
OnInit, czyli tworzymy obiekt głównego wspomnieć, jest konstruktor hw_mainwin.
S
./configure --prefix=/opt
okna (z przyciskiem oraz etykietą). Pełną Przyjmuje on szereg parametrów, takich
--with-gtk --enable-accel
treść implementacji zawiera Listing 2. W jak wielkość okna i jego styl. Definicje
Bardzo istotny dla naszego programu jest
metodzie OnInit funkcją SetTopWindow pominiętych stałych znajdują się w pli-
parametr --enable-accel (ta opcja jest
powodujemy, że nowo utworzone okno kach zródłowych (jak zawsze dostępnych
odpowiedzialna za skróty klawiszowe),
staję się głównym oknem naszej aplikacji. na płycie CD/DVD).
gdyż w przeciwnym razie nasz projekt nie
Zamknięcie tego okna to zamknięcie całej Zajmijmy się budowaniem interfejsu.
skompiluje się.
aplikacji. Wszystkie czynności z tym związane są
Ostatni sposób polega na wykorzy-
Po ustaleniu głównego okna pozosta- zawarte w konstruktorze hw_mainwin.
staniu nowej wersji GTK 2.2 bądz 2.4.
je nam wyświetlić je na ekranie polece- Pełną treść kodu konstruktora zawiera
Polecenie konfiguracji wygląda w nastę-
niem: win->Show(TRUE);. Gdyby zamiast Listing 4.
pujący sposób:
wartości TRUE w argumencie Show podać Zaczynamy od wywołania metody
S
./configure --prefix=/opt wartość FALSE, to okno zostałoby ukryte. wxFrame::Create i przekazania wszyst-
--with-gtk --enable-accel  enable-gtk2 Na koniec metody OnInit zwracamy kich parametrów konstruktora. Utwo-
wartość TRUE, co oznacza, że działanie rzyliśmy w ten sposób ramkę, bo tak
Niestety, w przypadku wxWidgets 2.4.2
aplikacji ma być kontynuowane. Jeśli w nomenklaturze wxWidgets nazywa się
nasz program bazy filmów nie kompiluje
podczas wykonywania wstępnych czyn- podstawowy typ okna.
się, gdy chcemy korzystać z tych wersji
ności napotkamy na jakieś problemy, np. Następne trzy linie kodu to utworze-
GTK. Mogą wystąpić także problemy
nie uzyskamy połączenia z bazą danych, nie tzw. pudełka. W bibliotece wxWid-
z przykładami. Nowa wersja z pewnością
to zawsze można zwrócić wartość FALSE. gets, podobnie jak w GTK+/GNOME,
będzie już znacznie bardziej dopracowana.
W takim przypadku działanie naszego
programu zostanie zakończone.
Listing 2. Implementacja aplikacji
(a jakże by inaczej) Hello World!. Komu- W implementacji zastosowaliśmy
#include "wx/wx.h"
nikat będzie wyświetlany poprzez makro IMPLEMENT_APP, w którym dokoń-
#include "hw.h"
widget wxStaticText  jest to bezpośred- czymy tworzenie stałych elementów klasy,
#include "hw_mainwin.h"
ni odpowiednik etykiety (ang. label) reprezentujących aplikację (zadeklarowa-
bool hw_app::OnInit(){
z innych bibliotek. Oprócz etykiety, ne wcześniej makrem DECLARE_APP).
S
hw_mainwin *win=new
w naszym pierwszym programiku znaj-
hw_mainwin(NULL);
SetTopWindow(win);
dzie się również przycisk, czyli wxButton. Etykieta i przycisk
win->Show(TRUE);
Pierwszą czynnością, którą należy Klasa hw_mainwin jest odpowiedzialna
return TRUE;
wykonać, jest utworzenie klasy reprezen- za nasze główne okno aplikacji. Listing
}
tującej całą aplikację. Klasa ta dziedziczy 3 zawiera jej definicję. Podobnie jak
IMPLEMENT_APP(hw_app)
z klasy wxApp i w naszym przypadku w klasie aplikacji, makrem DECLA-
71
www.lpmagazine.org
dla programistów
Obsługa zdarzenia  click
Listing 3. Klasa reprezentująca okno
W klasie hw_mainwin musimy jeszcze
class hw_mainwin: public wxFrame{
umieścić jedną specjalną tablicę, gdzie
DECLARE_CLASS( hw_mainwin )
powiążemy odpowiednie widgety ze
DECLARE_EVENT_TABLE()
zdarzeniami, które mogą wystąpić w apli-
public:
kacji. Najważniejszym elementem są
hw_mainwin();
hw_mainwin(wxWindow* parent, wxWindowID id = HW_MAINWIN_IDNAME, identyfikatory kontrolek. W naszym przy-
const wxString& caption = HW_MAINWIN_TITLE,
padku interesujący nas przycisk posiada,
const wxPoint& pos = HW_MAINWIN_POSITION,
nadany przez nas, identyfikator o nazwie
const wxSize& size = HW_MAINWIN_SIZE,
IB_EndBTN. Połączenie identyfikatora
long style = HW_MAINWIN_STYLE);
z odpowiednim zdarzeniem, czyli kliknię-
void OnIbEndbtnClick( wxCommandEvent& event );
... ciem na przycisk, wymaga zastosowania
};
makra EVT_BUTTON, natomiast sama tabela
zdarzeń prezentuje się następująco:
mamy do czynienia ze specjalnymi przekazać informacje o sposobie wyrów- BEGIN_EVENT_TABLE( hw_mainwin, wxFrame )
S
kontenerami, w których umieszczamy nania, np. jeśli chcemy, aby etykieta była EVT_BUTTON( IB_EndBTN, hw_mainwin::
widgety. Obiekty tego rodzaju samo- wyrównana do lewej strony, to podajemy OnIbEndbtnClick )
czynnie dbają o odpowiednie skalowanie wartość wxALIGN_LEFT. Zwróćmy w tym END_EVENT_TABLE()
komponentów czy ich wyrównywanie miejscu uwagę na to, że w przypadku
w poziomie bądz w pionie. Podstawo- tworzenia przycisku również przyjmuje Pozostało nam określenie postaci samej
wym komponentem jest pudełko wxBo- on identyczny zestaw parametrów. metody OnIbEndbtnClick. Zawiera ona
xSizer które, w zależności od podanego Pozostałe czynności dotyczące ety- jedną linię kodu, a jest nią wywołanie
,
parametru, może dbać o stosowne roz- kiety są już bardzo proste, gdyż ustalamy metody Close, które spowoduje zamknię-
mieszczenie widgetów w pionie bądz czcionkę: cie okna i w efekcie całej aplikacji:
w poziomie.
S
Przyjrzyjmy się bliżej, w jaki sposób label_hw->SetFont(wxFont(16, wxSWISS, void hw_mainwin::OnIbEndbtnClick
tworzymy etykietę: wxNORMAL, wxBOLD, FALSE)); ( wxCommandEvent& event ){ Close(TRUE);
}
S
wxStaticText* label_hw = new Ostatnią czynnością jest dodanie etykiety
S
wxStaticText( this, ID_LBL, do pudełka wywołaniem: box_sizer- Klasa zarządzania danymi
S
_("Hello World!"), wxDefaultPosition, >Add(...);. Jak widać, podobnie postępu-  data_class
wxDefaultSize, 0 ); jemy z przyciskiem. Również w naszym Zajmiemy się teraz naszą główną aplika-
docelowym programie będziemy postę- cją. Podstawowa klasa to niewątpliwie
Pierwszym argumentem jest odwołanie pować w bardzo podobny sposób. Z tego data_class. Zawiera ona kilkanaście
się do wskaznika na okno. Następnie powodu warto przeanalizować ten krótki metod (krótką charakterystykę kilku
podajemy identyfikator. W naszym przy- program, ponieważ w dalszej części arty- przedstawia Tabela 1). Oprócz dwóch
kładzie przyjął on następującą definicję: kułu skupimy się na innych (trudniejszych) metod: edonkey_hash oraz make_screen,
aspektach niż budowanie interfejsu. pozostałe składowe tej klasy odno-
#define ID_LBL 10002
Listing 4. Treść konstruktora hw_mainwin
Wartość identyfikatora może być inna,
ale autorzy biblioteki zalecają, aby były
wxFrame::Create( parent, id, caption, pos, size, style );
to numery większe od 10000. Wartości
wxBoxSizer* box_sizer = new wxBoxSizer(wxVERTICAL);
niższe są stosowane przez wewnętrzne
this->SetSizer(box_sizer);
mechanizmy biblioteki. Podobnie okre-
this->SetAutoLayout(TRUE);
ślamy identyfikator dla przycisku czy
S
dowolnego innego widgetu. Po okre- wxStaticText* label_hw = new wxStaticText( this, ID_LBL, _("Hello World!"),
wxDefaultPosition, wxDefaultSize, 0 );
śleniu identyfikatora podajemy treść,
jaka jest wyświetlana przez etykietę.
label_hw->SetFont(wxFont(16, wxSWISS, wxNORMAL, wxBOLD, FALSE));
Koniecznie korzystamy z makra _(), gdyż
w przeciwnym razie mielibyśmy kłopoty
box_sizer->Add(label_hw, 0, wxALIGN_CENTER_HORIZONTAL | wxALL | wxADJUST_MINSIZE, 5);
podczas kompilacji kodu. Kolejne para-
metry to pozycja etykiety oraz jej wiel- wxButton* button_ok = new wxButton( this, IB_EndBTN, _("OK"), wxDefaultPosition,
wxDefaultSize, 0 );
kość. Jak widać na Listingu 4, podane
box_sizer->Add(button_ok, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 5);
zostały wartości domyślnie. Za stero-
GetSizer()->Fit(this);
wanie położeniem i wielkością będzie
GetSizer()->SetSizeHints(this);
odpowiedzialne pudełko. Dość istotny
Centre();
jest ostatni parametr  styl. Można tam
72
sierpień 2004
baza danych filmów dla programistów
funkcję  wytrych o nazwie: sqlite_exec_ typu %s, %d czy %Q. W klasie data_class
Projektowanie interfejsu
printf. Nie bez powodu w swojej nazwie większość metod wykorzystuje tę cechę.
Biblioteka wxWidgets istnieje od ponad
zawiera ona nazwę standardowej funkcji Najprostszym przykładem jest metoda
10 lat. Doczekała się wielu dodatkowych
printf, gdyż, podobnie jak oryginal- delete_data_id, usuwająca rekord
narzędzi, a jednym z nich jest Dialog-
na funkcja printf, przyjmuje wzorzec, o wskazanym id. Jej kod przedstawia się
Blocks. Jest to program do budowania
w którym stosowane są znaki procentu, następująco:
interfejsu graficznego za pomocą myszki.
np. %d, oznaczający liczbę całkowitą.
Program generuje również całą klasę
S
oraz samodzielnie zarządza zdarzenia- Sposoby użycia sqlite_exec_printf db_error=sqlite_exec_printf
S
mi. Co najważniejsze, nie niszczy kodu najlepiej poznać na przykładach. Zobacz- (db, "delete from films where id=%d",
wpisanego przez programistę w innym
my, jak wygląda utworzenie tabeli (część 0, 0, &zErrMsg, id);
edytorze dopóty, dopóki nie usunie się
wyrażenia SQL została skrócona):
specjalnie oznaczonych komentarzy. Nie-
W drugim parametrze, w poleceniu SQL,
stety, program ten ma jedną zasadniczą
S
db_error=sqlite_exec_printf został umieszczony wzorzec %d (polecenie
wadę  jest narzędziem komercyjnym.
S
(db, "create table films ( przekształcenia dla liczby całkowitej). Po
Mimo wszystko zachęcam wszystkich
S
id integer primary key, wszystkich pięciu obowiązkowych parame-
do jego wypróbowania. Tym bardziej, że
S
... trach, podobnie jak w printf, musimy podać
wersja niezarejestrowana pozwala na
hash_edonkey char(50));",0,0,&zErrMsg); wartość naszej liczby, czyli zmienną id.
edycję naszej bazy danych. W zródłach
O jednym wzorcu %Q warto wspomnieć
znajduje się gotowy projekt do wykorzy-
Pierwszy argument sqlite_exec_print to raz jeszcze. Polecenia SQL wymagają, aby
stania (plik film_mainwin.pjd).
Tym z czytelników, którzy nie akcep- uchwyt reprezentujący bazę danych. Po dane tekstowe były objęte apostrofami.
tują takiego rozwiązania, polecam inny
nim podajemy ciąg znaków, czyli pole- Przekształcenie %Q spełnia ten warunek,
program, ale już w pełni darmowy i otwar-
cenie SQL przeznaczone do wykonania. uwalniając nas od żmudnego wstawiania
ty, o nazwie wxGlade. Nie jest on aż tak
Jak widać na powyższym przykładzie, apostrofów w tekście formatującym.
bardzo dopracowany jak DialogBlocks,
zakończone średnikiem. W trzecim argu- Ostatnim przykładem, który należy
ale potrafi generować kod nie tylko dla
mencie można podać funkcję callback, omówić, jest zastosowanie funkcji call-
C++, ale również dla Pythona i Perla
która będzie wywoływana podczas back. W naszym programie w taki
(czego nie potrafi DialogBlocks).
odbierania danych. Będziemy ją wykorzy- sposób będziemy przekazywać dane do
stywać w przypadku zapytań typu select, tabel wxGrid. Wzorcowym przykładem
szą się bezpośrednio do systemu baz gdyż create table nie zwraca żadnych jest metoda search_file_name:
danych. dodatkowych danych. Następny argument
S
Zanim zaczniemy korzystać z bazy, służy do podania dodatkowego parame- void data_class::search_file_name(char
należy uzyskać do niej dostęp. Otwarcie tru, który zostanie pózniej przekazany *fn, sqlite_callback fnc, void *d ) {
S
bazy następuje w konstruktorze tej klasy do funkcji zwrotnej. Piątym w kolejności db_error=sqlite_exec_printf(db, "select
S
i przedstawia się następująco: elementem wywołania jest już znana * from films where file_name
S
zmienna zErrMsg, w której, w przypadku like '%s%'",
S
zErrMsg=0; wystąpienia błędu, zostanie umieszczony fnc, d, &zErrMsg, fn);
db = sqlite_open(fdb_name, 0, &zErrMsg); wskaznik do tekstu komunikatu. }
if( db == 0 )db_error=-1; Po tych pięciu obowiązkowych para-
metrach mogą wystąpić następne, jeśli Metoda przyjmuje, podobnie jak dwie
W zmiennej zErrMsg znajdzie się wskaza- w poleceniu SQL zostały użyte wzorce pozostałe: search_title oraz search_
nie na tekst komunikatu o błędzie. Funkcja
sqlite_open samoczynnie założy plik bazy
Tabela 1. Najważniejsze metody klasy data_class
danych w momencie, gdy nie będzie on
Metoda Opis
istniał. Warto też pamiętać, że funkcja
data_class(char *fdb_name); konstruktor otwiera istniejącą bazę danych
sqlite_open (oraz pozostałe z API Sqlite)
lub zakłada nową
dają wartość jeden, gdy nie nastąpiły błędy.
void add_data_full(char *file_name, ... ); dodaje do bazy danych jeden pełny rekord
Jak na razie drugi parametr tej funkcji,
razem ze zrzutem ekranowym
gdzie podano zero, nie jest używany przez
void get_data( sqlite_callback, void *d ); pobiera wszystkie dane (metoda przezna-
API. Być może w przyszłości będzie można
czona do wypełniania tabel  wxGrid)
poprzez ten parametr wskazać, że baza ma
void delete_data_id(int id); usuwa rekord o wskazany ID
być używana w trybie tylko do odczytu.
Zamknięcie bazy danych to jedna
void search_title(char *t, sqlite_callback, szuka w bazie filmu to tytule
instrukcja: sqlite_close(db);. Ponieważ
void *d );
nasza klasa używa konstruktora do
void edonkey_hash(char *fname, wyznacza sumę kontrolną (hash, odcisk) dla
otwarcia bazy, to zamknięcie bazy nastę-
char *hash); podanego pliku
puje w destruktorze.
void make_screen(char *fname_video,  wyciąga klatkę z filmu i zapisuje ją pod
Może wydawać się, że różne typy zapy-
char *pic_file, char *codec_fourcc); podaną nazwą jako plik jpeg
tań mogą wymagać różnego podejścia,
void create_table() utworzenie tabeli
ale autorom Sqlite udało się przygotować
73
www.lpmagazine.org
dla programistów
pimy się tylko na plikach typu mpg, avi
Listing 5. Zapis danych do pliku jpeg przy użyciu biblioteki DevIL
i podobnych.
void DevILSaveTo(char *fname, int width, int height, void *ptr){
Zanim wyciągniemy z filmu jakie-
ILuint imageID;
kolwiek dane, tworzymy cztery ważne
ilGenImages(1, &imageID);
zmienne:
ilBindImage(imageID);
ilTexImage(width, height, 1, 3, IL_RGB, IL_UNSIGNED_BYTE, ptr);
xine_t *xine;
iluFlipImage();
iluScale(160, 100, 1);
xine_stream_t *stream;
ilEnable(IL_FILE_OVERWRITE);
xine_vo_driver_t *vo_driver;
ilSaveImage(fname);
xine_ao_driver_t *ao_driver;
ilDeleteImages(1, &imageID);
}
Następnie dokonujemy ich inicjalizacji.
W tym miejscu trzeba zwrócić uwagę
director, trzy parametry. Pierwszy to w następujący sposób: jeśli odczytamy na wykorzystanie sterowników audio
nazwa pliku, który chcemy odnalezć. wartość 120, to w tablicy msg umieścimy i wideo. Dzwięk nie jest nam potrzebny,
Następny to funkcja callback. Będzie ona tekst w postaci %120. Wykonuje to nastę- dlatego poza pierwszym parametrem,
wywoływana przy każdym znalezionym pującą pętla: w którym podajemy uchwyt do biblio-
rekordzie. Pozwoli to na przepisanie teki, w dwóch pozostałych podajemy
rekordów do tabeli. Ostatni parametr j=0; wartości NULL (wartość ta oznacza rów-
o nazwie d umożliwi przekazanie wska- for(i=0;izania na obiekt wxGrid. Funkcję fnc memset(value, 0, 16); odpowiedni sterownik). Nie możemy
bezpośrednio przekazujemy do sqlite_ sprintf(value, "%%%3d", *(tbl + i)); tak postąpić w przypadku obrazu.
exec_printf. W dalszej części omówię msg[j++]=value[0]; msg[j++]=value[1]; W drugim parametrze funkcji xine_
dokładniej postać funkcji zwrotnej na msg[j++]=value[2]; msg[j++]=value[3]; open_video_driver podajemy identyfi-
przykładzie wyszukiwania danych. } kator sterownika, przy pomocy którego
chcemy wyświetlać obraz. My nie chcemy
Zapis klatki do bazy Zadanie konwersji z wartości binarnej oglądać filmu, więc podajemy wartość
danych na tekstową zrzuciliśmy na barki funkcji none. Podobnie postępujemy w trzecim
W następnym punkcie pokażę, jak sprintf i ciąg przekształcający %%%3d. parametrze, gdzie dokładniej określamy
uzyskać klatkę z pliku wideo. Wcze- W zmiennej value znajdzie się tekst rodzaj i typ urządzania, np. dla środowiska
śniej trzeba wspomnieć o sposobie w dokładnie takiej postaci, jak wcześniej X czy biblioteki GTK. Podaliśmy tu wartość
wprowadzania danych do bazy. Klatka założyliśmy. Po wykonaniu całej pętli XINE_VISUAL_TYPE_NONE, co oznacza, że
filmu zostanie zapisana do pliku w for- tablica msg będzie wypełniona wartościa- film będzie dekodowany do wewnętrzne-
macie JPG. Niestety, Sqlite w wersji 2.8 mi ASCII, więc bez kłopotów można je go bufora w pamięci RAM. Kod inicjalizacji
nie obsługuje w wygodny sposób przekazać w wywołaniu funkcji sqlite_ jest następujący:
danych binarnych i wymaga od nas exec_printf.
konwersji do formatu ASCII (3.0 już xine=xine_new();
radzi sobie z tym typem danych, ale Aapanie klatki filmu xine_init(xine);
podczas pracy nad artykułem dostępna Prawdopodobnie najciekawszą funkcją ao_driver = xine_open_audio_driver
była tylko wersja alpha). Jest to spo- naszej bazy danych jest przechowywa- (xine, NULL, NULL);
wodowane m.in. tym, że do wpisania nie jednej klatki z filmu. Zanim dołą- vo_driver = xine_open_video_driver
nowego rekordu wykorzystamy polece- czymy do naszej bazy plik zawierający (xine, "none", XINE_VISUAL_TYPE_NONE,
nie insert. klatkę (w niskiej rozdzielczości, bo tylko NULL);
Wpisaniem pełnego rekordu wraz 160x100), należy znalezć w miarę łatwy stream=xine_stream_new
z obrazem zajmuje się metoda add_data_ sposób na uzyskanie obrazu. Wyko- (xine, ao_driver, vo_driver);
full. rzystamy w tym celu bardzo dobrą
Na początku otwieramy plik: f=o- bibliotekę, a mianowicie Xine. Służy Teraz możemy już otworzyć plik z filmem.
pen(scr_filename, O_RDONLY);. Do ona do odtwarzania różnego rodzaju Wystarczy tylko jedna linia kodu:
zmiennej len zapisujemy długość pliku. plików audio/wideo, w tym także płyt
Możemy w ten sposób przydzielić odpo- DVD czy VCD. My, dla ułatwienia, sku- error=xine_open(stream, fname_video);
wiednią ilość pamięci:
Listing 6. Odczytywanie klatki filmu z bazy danych
tbl=new unsigned char[len];
s=DataTableGrd->GetCellValue(event.GetRow(), 0);
msg=new char[(len*5)];
s.ToLong(&id);
memset(msg, 0, (len*5));
main_db->get_blob_for_id((int)id, "/tmp/pic.jpg", callback_for_blob);
if(main_db->error_level()==0){
bmp.LoadFile(_("/tmp/pic.jpg"), wxBITMAP_TYPE_JPEG);
Do tablicy tbl wczytujemy całą zawartość
FrameBMP->SetBitmap(bmp);
pliku: read(f, (void*)tbl, len);. Pozosta-
}
je nam teraz dokonać konwersji danych
74
sierpień 2004
baza danych filmów dla programistów
Rysunek 2. Projektowanie interfejsu w DialogBlocks
S
W bardzo prosty sposób odczytujemy (stream, &width, &height, &ratio,
informacje, które mogą przydać się &format, yuv);
w naszej bazie danych, np. bitrate filmu: xine_stop(stream);
bitrate=xine_get_stream_info Pierwsze wywołanie xine_get_cur-
(stream, XINE_STREAM_INFO_VIDEO_BITRATE); rent_frame jest konieczne, aby poznać
wymiary klatki i współczynnik ratio,
W metodzie wyciągającej pojedynczą czyli stosunek szerokości do wysokości
klatkę dokonujemy także odczytu cał- obrazu. W ostatnim parametrze, gdzie
kowitej długości filmu. Sprowadza się to zostałyby umieszczone dane o obra-
do zastosowania funkcji xine_get_pos_ zie, podajemy wartość NULL. Z tego
length. W ostatnim (czwartym) parame- powodu, po pierwszym wywołaniu
trze otrzymamy całkowitą długość filmu. funkcji wyciągającej klatkę z filmu, przy-
Tę wartość (są to milisekundy) należy pisujemy odpowiednią ilość pamięci do
podzielić przez tysiąc, a potem przez zmiennej yuv. Ta zmienna, jak wskazuje
sześćdziesiąt, aby otrzymać czas w minu- jej nazwa, będzie przechowywać dane
tach. Przedstawia się to następująco: obrazu w formacie YUV. Zapis do for-
matu JPG będzie wymagał od nas kon-
S
xine_get_pos_length (stream, wersji do formatu RGB.
S
&tmp_pos_stream, &tmp_pos_time, Po alokacji pamięci drugie wywoła-
&tmp_length_time); nie xine_get_current_frame umieści w
S
film_length=(int)trunc((tmp_length_time zmiennej yuv obraz gotowy do dalszej
/1000.00f) / 60.00f); obróbki. Funkcją xine_stop wyłączamy
odtwarzanie filmu.
Nas interesuje jedna klatka z filmu. Aby Dalsze czynności są związane z kon-
ją przechwycić, należy nakazać odtwa- wersją kolorów YUV do formaty RGB.
rzanie filmu i użyć dwa razy funkcji Zainteresowanych Czytelników odsyłam
xine_get_current_frame: do kodu zródłowego data_class.cpp
 kod dokonujący konwersji został zapo-
xine_play(stream, 3000, 0); życzony z programu Totem. Polecam
S
xine_get_current_frame analizę kodu, szczególnie pliku bacon-
S
(stream, &width, &height, &ratio, video-widget-xine.c.
&format, NULL); Nie jest to ostatni problem, gdyż
S
yuv = (uint8_t*) malloc musimy przecież zapisać dane klatki filmu
((width + 8) * (height + 1) * 2); do pliku. Tutaj z pomocą przychodzi nam
S
xine_get_current_frame biblioteka DevIL, już przedstawiana pod-
75
www.lpmagazine.org
dla programistów
Natomiast sam kod wypełniający tabelę
przedstawia się następująco:
dlg->select_cell_work=0;
grid->BeginBatch();
main_db->get_data( (sqlite_callback)
grid_fill_mainframe, (void*)grid);
grid->EndBatch();
dlg->select_cell_work=1;
Zmiana wartości select_cell_work
jest bardzo ważna, ponieważ podczas
wypełniania jest generowane zdarzenie
OnSelectCell. Okno edycji wykorzystuje je
do odczytywania klatki z filmu. Nadanie
zmiennej select_cell_work wartości zero
sprawi, że obsługa zdarzenia nie będzie
Rysunek 3. Okno przeglądania danych
odczytywać danych o klatce z filmu.
Po tych przygotowaniach możemy po-
czas pisania przeglądarki plików graficz- o nazwie ed2k-hash, a dokładniej jego 11 kazać okno na ekranie: dlg->ShowModal();.
nych dla konsoli fbcon (poprzedni numer plików, przy czym najistotniejszym jest W samym oknie edycji mamy tabelę,
Linux+). Zapis danych prezentuje Listing 5. processfile.c (oryginalny program ed2k- w której znajdują się wszystkie pola
Kod, jak sądzę, nie wymaga większego hash jest napisany w ANSI-C, natomiast i rekordy wpisane do bazy, ale klatka
komentarza, ale na jeden element trzeba pliki w kodzie zródłowym na płytach z filmu jest prezentowana poniżej tabeli.
zwrócić uwagę, a mianowicie funkcję ilu- CD/DVD zostały już poprawione i dopa- Kliknięcie na inny wiersz powoduje jej
FlipImage(). Otóż tak się składa, że prze- sowane do C++). W tym pliku znajduje wyświetlanie, jeśli wprowadzono ją do bazy
chwycona przez nas klatka obrazu jest się funkcja processfile. Dzięki niej obli- danych. Listing 6 prezentuje najważniejszy
odwrócona do góry nogami, więc musimy czenie odcisku sprowadza się do trzech fragment obsługi dla tego zdarzenia.
ją obrócić. Właśnie to robi wymieniona linijek kodu:
funkcja. Następnie funkcja iluScale doko- Podsumowanie
nuje przeskalowania obrazu. fileinfo info; Tego typu program można wyposażyć
process_file(fname, &info); w wiele rozszerzeń, do czego bardzo
Wyznaczamy odcisk strcpy(hash, info.ed2k_hash_str); zachęcam. Na początek warto dodać
Wyciągnięcie klatki z filmu nie okazało dodatkowe pola, np. rozdzielczość (to
się łatwym zadaniem, lecz z pomocą Dokładnie w taki sposób została zde- względnie łatwe zadanie). Na łamach
Xine oraz pożyczonego kodu z programu finiowana metoda edonkey_hash klasy Linux+ z pewnością będziemy jeszcze
Totem, problem udało się nam rozwiązać. data_class. powracać do tematu baz danych. Takie
Niestety, wyznaczanie odcisku także nie środowiska, jak GNOME czy KDE,
jest trywialnym zadaniem. Jego algorytm Okno przeglądania i edycji z dnia na dzień dysponują coraz lepszymi
nie jest prosty w implementacji. Musimy danych mechanizmami dostępu do baz danych,
znów pożyczyć kod. Jest to tym bardziej Nie mówiliśmy dotąd zbyt wiele które będzie warto bliżej poznać.
uzasadnione, że odcisk (ang. hash) sto- o samym interfejsie użytkownika, więc
sowany w sieci EDonkey nie jest stan- w tym miejscu zobaczymy, że dzięki
W Internecie:
dardowym algorytmem typu MD5 czy klasie data_class bardzo łatwo go
SHA-1, ale pewną modyfikacją algorytmu zintegrujemy z systemem baz danych.
" Biblioteka wxWidgets:
MD4. Wykorzystamy dodatkowy pakiet W klasie film_mainframe, zanim na
http://www.wxwidgets.org/
ekranie zostanie wyświetlone okno
" System baz danych Sqlite:
do edycji danych, wykonujemy dwie
http://www.sqlite.org/
rzeczy: wypełniamy kolumny odpo-
" Biblioteka Xine:
wiednimi etykietami oraz wpisujemy
http://xinehq.de/
do tabeli wszystkie rekordy. Przykład " Strona projektu ed2k-hash:
http://ed2k-tools.sourceforge.net/
dla dwóch pierwszych kolumn jest
index.shtml
następujący:
" Program DialogBlocks:
http://www.anthemion.co.uk/
grid->SetColLabelValue(0, _("ID"));
dialogblocks/
grid->SetColSize(0,50);
Rysunek 4. Okno dialogowe " Następny program do generowania
S
grid->SetColLabelValue(1,
interfejsu, ale w pełni darmowy:
wskazywania pliku wideo, z którego
S
_("Nazwa pliku")); grid->SetColSize
http://wxglade.sourceforge.net/
zostanie wyciągnięta jedna klatka filmu
(1,250);
76
sierpień 2004


Wyszukiwarka

Podobne podstrony:
2004 08 Flagi sterujące optymalizacją w GCC [Programowanie]
08 wprowadzenie do programowania grafikiidu39
2004 01 Pisanie bezpiecznych programów [Programowanie]
2004 08 Tuning KDE i GNOME [Poczatkujacy]
Dz U 2004 198 2042 zmiana z dnia 2004 08 27
program szkolenia specjalistycznego www katalogppoz pl
2002 08 Programowana tablica świetlna
MS Program Laboratorium 08
2004 09 Rozszerzanie możliwości przeglądarek WWW [Programowanie]
program cwiczenia 08 2009 lekarski[1]
Ogranicz 2004 07 08
2004 07 Konsolowa przeglądarka plików graficznych [Programowanie]
2004 10?lipse i Java–program do obliczania sum kontrolnych [Programowanie]
program szkolenia podstawowego www katalogppoz pl
2004 02 Aplety dla GNOME [Programowanie]
2004 01 08 Dec 2 MON – Dokumenty normalizacyjne

więcej podobnych podstron