2008.06_MiniCommander – własne dwa panele_[Programowanie].pdf
(
149 KB
)
Pobierz
439032723 UNPDF
Programowanie
Arkusz kalkulacyjny i makra
własne dwa panele
Marek Sawerwain
Zarządzanie plikami to jedna z najważniejszych i zarazem najczęściej wykonywana czynność podczas
korzystania z systemu operacyjnego, czyli mówiąc wprost podczas korzystania z komputera. Dla
systemu Linux, powstało już wiele różnych wersji tego typu programów. Niewątpliwie najbardziej
popularny jest program o nazwie Midnight Commander. Wzorcem dla tego programu był Norton
Commander, który w czasach systemu DOS znacząco poprawiał komfort korzystania z systemu.
nie był jedynym programem do zarządza-
nia plików. Drugim tego typu programem
o nieco innej fi lozofi i pracy był program
XTree. Podobnie jak Norton Commander również posiadał za-
gorzałych fanów. Kolejnym programem do zarządzania, który
także cieszył się ogromną popularnością był Directory Opus.
Jednakże był on dostępny na komputery Amiga. Użytkownicy
Linuksa są w bardzo komfortowej sytuacji, ponieważ powstało
kilkanaście różnych programów do zarządzania plikami. Istnie-
ją odpowiedniki Norton Commandera, jak wspomniany Midni-
ght Commander oraz równie dobry - jeśli nie lepszy - Krusader.
Wersji programu naśladującego XTree jest kilkanaście, powstał
nawet odpowiednik Directory Opusa - nazywa się Worker.
Natomiast naszym zadaniem będzie napisanie programu
podobnego do Midnight Commandera. Skupimy się tylko na
najważniejszych opcjach. Jednak będziemy mieli na uwadze
fakt, aby nasz program był użyteczny, czyli potrafi ł kopiować
pliki oraz tworzyć i kasować katalogi. Niestety nie jest możli-
we nawet pobieżne przedstawienie wszystkich elementów ko-
du źródłowego - jest on zbyt duży. Dlatego zostaną przedsta-
wione tylko podstawowe elementy.
Podstawowe informacje
W naszym programie będą dostępne tylko podstawowe opcje,
jednak mimo tych ograniczeń kod źródłowy liczy sobie 44kb.
To naturalnie nadal niewielki program, ale koniecznie trzeba
przygotować diagram najważniejszych zdarzeń. Rysunek 1
przedstawia taki schemat.
Pierwsze zdarzenie jakie pojawia się po uruchomieniu
programu dotyczy obsługi trybu tekstowego. Musimy od-
powiednio uruchomić obsługę konsoli. Następnie nasz pro-
gram wchodzi w główną pętlę. W pętli sprawdzamy, które
klawisze zostały użyte przez użytkownika. Jeśli używane są
kursory, to na aktywnym panelu odpowiednio przesuwamy
kursor wskazujący na aktualny katalog bądź plik. Wciśnię-
cie klawisza
TAB
powoduje zmianę panelu ma przeciwny.
Obsługujemy także inne klawisze, jak np.:
F7
, czyli two-
rzenie katalogu. Po naciśnięciu tego klawisza wyświetlamy
odpowiednie okno dialogowe. Obsługujemy także kombi-
nacje klawiszy, jak np.:
CTRL-A
- wciśnięcie tych klawiszy
powoduje pojawienie się okna do zmiany atrybutów pliku
lub katalogu.
Schemat z Rysunku 1 nie przedstawia wszystkich klawi-
szy, jednakże pozostałe klawisze są obsługiwane w podobny
56
czerwiec 2008
MiniCommander –
W
arto przypomnieć, iż Norton Commander
Programowanie
Arkusz kalkulacyjny i makra
sposób. Na koniec naturalnie trzeba zamknąć
tryb tekstowy.
Kod źródłowy naszego programu został
podzielony na kilka części. Pliki
fList.c
oraz
fList.h
to implementacja dynamicznej listy
dwukierunkowej, która pomaga w realizacji li-
sty plików i katalogów wyświetlanych w pane-
lu. Obsługa okien dialogowych została umiesz-
czona w plikach
dialogs.c
oraz
dialogs.h
. Re-
alizacja operacji na plikach i katalogach, np.:
usuwanie katalogu, to zadanie dla funkcji znaj-
dujących się plikach
fs_oper.c
oraz fs_op
er.h
.
Obsługa paneli, czyli ich rysowanie oraz ob-
sługa klawiatury została umieszczona w pli-
kach
workscr.c
i
workscr.h
. Ostatnim plikiem
jest
maincode.c
. W tym pliku znajduje się funk-
cja
main
, w której wykonujemy wszystkie nie-
zbędne czynności, aby uruchomić nasz pro-
gram, czyli uruchamiamy tryb tekstowy, a po
wciśnięciu klawisza
F10
, kończącego pracę na-
szego programu, w funkcji main wykonywane
są ostatnie czynności związane z zamknięciem
trybu tekstowego.
do katalogu. Naturalnie tylko wtedy, kiedy
mamy do tego prawo. Tę sytuację łatwo wy-
kryć sprawdzając choćby, czy wartością funk-
cji
opendir
jest wartość NULL. Jeśli tak jest,
może to oznaczać, iż nie mamy prawa dostę-
pu do katalogu. Gdy operacja się udała, za po-
mocą wywołań
readdir
będziemy odczyty-
wać zawartość katalogu, element po elemen-
cie. Poszczególne elementy są typu
struct
dirent
. Struktura ta opisuje jeden wpis w ka-
talogu. Zapisem do listy zajmuje się funkcja
DecomposeDirent
, która wydziela ze struktu-
ry dirent poszczególne elementy, jak nazwę i
rodzaj wpisu: plik, katalog, link symbolicz-
ny. Odczytywane są także prawa dostępu oraz
czas modyfi kacji. Te elementy są wyświetla-
ne w panelu. Proces odczytywania zakończy
się w momencie, gdy funkcja readdir zwróci
wartość NULL.
Odczytana zawartość katalogu niestety nie
jest posortowana - jej kolejność jest zazwyczaj
określona przez datę utworzenia. My jednak
będziemy sortować alfabetycznie: posortowa-
niem odczytanej zawartości zajmuje się funk-
cja
SortList
.
Listing 2. zawiera pełny kod źródłowy funk-
cji kopiującej plik. Funkcja ta posiada cztery
argumenty. Pierwsze dwa to ścieżka źródło-
wa oraz ścieżka docelowa. Trzeci argument to
wielkość kopiowanego pliku. W ostatnim ar-
gumencie podajemy prawa pliku.
Sam proces kopiowania polega na przepi-
sywaniu zawartości pliku małymi fragmenta-
mi. Jeden fragment to około 64kb. Dlatego na-
leży obliczyć ile bloków danych, czyli małych
fragmentów, trzeba przegrać. Naturalnie trze-
ba obliczyć resztę z dzielenia (ponieważ rzad-
ko bywa aby plik miał idealną długość po-
dzielną przez pewną wielkość):
blocks=len / 65000
remaing=len % 65000;
Po tych obliczeniach w pętli kopiujemy blo-
ki danych:
for(i=0;i<blocks;i++) {
le=read(f1, buf, 65000);
le=write(f2, buf, 65000);
}
Odczytanie zawartości katalogu
Zanim poszczególne nazwy zostaną wyświe-
tlone w panelu, należy odczytać zawartość ka-
talogu. Funkcja, która wykonuje to zadanie
jest dość krótka. Przedstawiona została na Li-
stingu 1. Odczyt zawartości wskazanego ka-
talogu jest dość łatwy do zrealizowania. Wy-
wołując funkcję
opendir
uzyskujemy dostęp
Kopiowanie pliku
Tworzony przez nas program potrafi kopiować
pojedyncze pliki oraz całe katalogi. Najważ-
niejszą funkcją jest funkcja kopiująca poje-
dynczy plik, ponieważ funkcja kopiowania re-
kurencyjnego korzysta z opisywanej funkcji.
Kopiowanie ostatniego fragmentu danych jest
podobne, wykorzystujemy zmienną
remaing
,
w której mamy informacje ile danych trzeba
przegrać, aby plik był kompletny.
le=read(f1, buf, remaing);
le=write(f2, buf, remaing);
START
Po przegraniu danych ustalamy jeszcze pra-
wa za pomocą polecenia
fchmod
. Zwróćmy
też uwagę (potwierdza to Listing 2.), iż jeśli
plik jest mniejszy niż wielkość jednego bloku
danych, to kopiujemy jego zawartość jednym
wywołaniem funkcji
read
oraz
write
.
inicjalizacja
trybu tekstowego
odczyt aktualnego
katalogu
TAK
Czy naciśnięto
klawisz TAB?
zmiana panelu
NIE
obsługa klawiatury
Biblioteka S-Lang
Tworzenie
katalogu?
TAK
procedura
tworzenia katalogu
Biblioteka
S-Lang
oferuje funkcje do obsługi
konsoli, czyli ekranu oraz klawiatury. Funk-
cje te pracują zarówno na niskim i wysokim
poziomie, co oznacza, że jeśli stosujemy
funkcję wysokiego poziomu nie musimy się
martwić o detale związane z obsługą konso-
li. Mamy także dostęp do prostego edytora
liniowego (Readline), co znacząco ułatwia
odbiór danych od użytkownika. Zaletą pa-
kietu
S-Lang
jest również wydajność. Pakiet
ten oferuje również dostęp do interpretowa-
nego języka programowania podobnego do
C. W naszym programie jednak nie korzy-
stamy z tej możliwości.
Edycja
atrubutów
TAK
procedura
edycji atrybutów
NIE
...
NIE
koniec pracy
programu?
TAK
zamknięcie
trybu tekstowego
STOP
Rysunek 1.
Schemat przepływu sterowania w projektowanej aplikacji
www.lpmagazine.org
57
Programowanie
Arkusz kalkulacyjny i makra
Listing 1.
Odczytanie zawartości katalogu
Lista plików i katalogów w panelu Listing
3. zawiera fragment z funkcji
DisplayDirec-
tory
, która jest odpowiedzialna na wyświetla-
nie listy plików i katalogów w lewym panelu
(wersja dla prawego jest naturalnie podobna).
Stosujemy pętlę for, która wykorzystuje zmien-
ne
ifrom_dir_left
oraz
ito_dir_left
. W
zmiennych tych zapisane są numery, od któ-
rego wpisu w katalogu należy rozpocząć wy-
świetlanie i na którym należy zakończyć. Ilość
elementów jest wyliczana przez funkcję
Cal-
culate
. Funkcja ta, uwzględniając wielkość, a
dokładniej wysokość konsoli, oblicza wielkość
okna podglądu. Zmienne
ifrom_dir_left
oraz ito_dir_left
są również odpowiednio
modyfi kowane. Gdy użytkownik naciska kla-
wisze strzałek, wtedy przesuwany jest kursor,
czyli zmienna
cpos_left.
Zmienne te są też
modyfi kowane, gdy naciskane są klawisze
Pa-
ge Up
oraz
Page Down
. Obsługa klawiszy znaj-
duje się w funkcji
NavigateKey
.
Wyświetlanie informacji o plikach (zebra-
nych przez funkcję
ReadDirectory
) rozpoczy-
namy od odczytania odpowiedniego elementu z
listy funkcją
At0List
. Następnie w zależności
int
ReadDirectory
(
char
*
path
)
{
DIR
*
dir
=
NULL
;
struct
dirent
*
idir
=
NULL
;
TDirItem
tmp
;
dir
=
opendir
(
path
);
if
(
dir
==
NULL
)
{
return
-
1
;
}
while
(
idir
=
readdir
(
dir
))
{
DecomposeDirent
(
idir
);
}
closedir
(
dir
);
SortList
(
active_dir
,
IDirCmp
,
IDirAssign
,
&
tmp
);
return 0;
}
Listing 4.
Odczytanie jednej linii tekstu, wykorzy-
stując edytor Readline
char
*
ReadLineText
(
int
x
,
int
y
,
int
w
,
char
*
txt
)
{
int
n
;
unsigned
char
*
line
;
rline_BUF
[
0
]=
0
;
init_slang_readline
(
x
,
y
,
w
);
if
(
txt
!=
NULL
)
{
SLrline_set_line
(
rline_RLI
,
txt
);
SLrline_set_point
(
rline_RLI
,
strlen
(
txt
));
}
line
=
SLrline_read_line
(
rline_
RLI
,
NULL
,
&
len
);
strcpy
(
&
rline_BUF
[
0
]
,
line
);
SLfree
(
line
);
SLrline_close
(
rline_RLI
);
return
&
rline_BUF
[
0
];
}
Listing 3.
Funkcja kopiująca plik
case
PANEL_LEFT
:
for
(
i
=
ifrom_dir_left
;
i
<=
ito_
dir_left
;
i
++)
{
di
=(
TDirItem
*)
At0List
(
dir_
left
,
i
);
switch
(
di
->
ft
)
{
case
ft_DIR
:
sprintf
(
t
,
DISP_DIR
,
di
->
name
,
di
-
>
mtime
);
break
;
case
ft_LNK
:
sprintf
(
t
,
DISP_LNK
,
di
->
name
,
di
-
>
mtime
);
break
;
case
ft_REG
:
sprintf
(
t
,
DISP_REG
,
di
->
name
,
IntToMB
(
di
->
size
)
,
di
->
mtime
);
break
;
default
:
sprintf
(
t
,
"<unk>"
);
}
if
(
i
==
cpos_left
)
{
Slsmg_set_color
(
3
);
SLsmg_gotorc
(
rd
++
,
1
);
Slsmg_printf
(
"%s"
,
t
);
/* dodatkowe informacje na
dole okna*/
Slsmg_set_color
(
5
);
SLsmg_gotorc
(
SLtt_Screen_
Rows
-
2
,
2
);
sprintf
(
t
,
ADD_INFO
,
di
->
name
,
ReturnMode
(
di
->
mode
)
,
IntToMB
(
di
->
size
));
SLsmg_printf
(
"%s"
,
t
);
}
else
{
SLsmg_set_color
(
5
);
SLsmg_gotorc
(
rd
++
,
1
);
Slsmg_printf
(
"%s"
,
t
);
}
}
break
;
Listing 2.
Funkcja kopiująca plik
int
copy_fi le
(
char
*
from
,
char
*
to
,
int
len
,
mode_t
mode
)
{
int
f1
,
f2
,
i
,
blocks
,
remaing
,
le
;
void
*
buf
;
le
=-
1
;
f1
=
open
(
from
,
O_RDONLY
);
if
(
f1
!=-
1
)
{
f2
=
open
(
to
,
O_CREAT
|
O_TRUNC
|
O_WRONLY
);
if
(
f2
==-
1
)
{
le
=
close
(
f1
);
return
-
1
;
}
buf
=(
void
*)
malloc
(
65000
);
if
(
len
<
65000
)
{
le
=
read
(
f1
,
buf
,
len
);
le
=
write
(
f2
,
buf
,
len
);
le
=
fchmod
(
f2
,
mode
);
}
else
{
blocks
=
len
/
65000
;
remaing
=
len
%
65000
;
for
(
i
=
0
;
i
<
blocks
;
i
++)
{
le
=
read
(
f1
,
buf
,
65000
);
le
=
write
(
f2
,
buf
,
65000
);
}
le
=
read
(
f1
,
buf
,
remaing
);
le
=
write
(
f2
,
buf
,
remaing
);
le
=
fchmod
(
f2
,
mode
);
}
free
(
buf
);
le
=
close
(
f2
);
le
=
close
(
f1
);
}
else
{
le
=-
1
;
}
return
le
;
}
Listing 5.
Okno kasowania pliku
int
DeleteDLG
(
char
*
path
)
{
int
x
,
y
,
c
,
l
,
le
;
l
=
strlen
(
path
)+
6
+
2
;
if
(
l
<
28
)
l
=
28
;
x
=
SLtt_Screen_Rows
/
2
;
y
=
SLtt_Screen_Cols
/
2
;
SLsmg_fi ll_region
(
x
-
2
,
y
-
l
/
2
,
4
,
l
+
2
,' '
);
SLsmg_draw_box
(
x
-
2
,
y
-
l
/
2
,
4
,
l
+
3
);
SLsmg_gotorc
(
x
-
1
,
y
-
l
/
2
+
1
);
SLsmg_printf
(
"Delete
\"
%s
\
"
"
,
path
);
SLsmg_gotorc
(
x
,
y
-
9
);
DrawButton
(
"OK"
);
SLsmg_gotorc
(
x
,
y
);
DrawButton
(
"CANCEL"
);
Slsmg_refresh
();
if
(
Select
(
x
,
y
-
9
,
y
,
"OK"
,
"CANCEL"
)==
0
)
{
le
=
recursive_del
(
path
);
if
(
le
==-
1
)
AccessViolationDLG
();
}
}
58
czerwiec 2008
Programowanie
Arkusz kalkulacyjny i makra
od typu wpisu (plik, link, katalog) przygotowu-
jemy poleceniem
sprintf
ciąg znaków.
Kolejny test to sprawdzenie, czy aktualna
wartość zmiennej i jest równa wartości zmien-
nej
cpos_left
. Jeśli tak jest, to dobierając od-
powiednie zmiany kolorów użytkownik nasze-
go programu będzie widział kursor w panelu.
Inaczej mówiąc, wiadomo będzie na jaki plik
bądź katalog aktualnie wskazujemy. Jeśli bę-
dzie to katalog, to naciśnięcie klawisza
ENTER
spowoduje przejście do tego katalogu. Wybranie
dwóch kropek, naturalnie, spowoduje przejście
katalog wyżej.
przez argument
w.
Natomiast w ostatnim argu-
mencie o nazwie
txt
możemy podać tekst, któ-
ry zostanie podany edycji. Zmieniony tekst lub
jego kopia, jeśli użytkownik nie zmieni treści,
staje się wynikiem działania funkcji
ReadLine-
Text
. Funkcja
ReadLineText
choć dość krótka,
składa się z trzech części. W pierwszej inicjali-
zujemy system
Readline
dostępny w bibliote-
ce
S-Lang
. Następnie sprawdzamy zawartość
argumentu
txt
. Jeśli wartość tej zmiennej jest
różna od NULL, to zawartość argumentu
txt
zostanie przepisana do edytora liniowego za
pomocą funkcji
Slrline_set_line
.
Następny krok to edycja danych. Po wywo-
łaniu funkcji
SLrline_read_line
użytkownik
może wpisać nowy tekst lub zmienić istniejący.
Proces edycji trwa do momentu wciśnięcia kla-
wisza
ENTER
. Można też tak oprogramować
obsługę
Readline
, aby klawiszem
TAB
można
było opuścić tryb edycji. Po zakończonej edycji
celowo zawartość zmiennej line jest kopiowa-
na do zmiennej globalnej
rline_BUF
, bowiem
pamięć przydzielona do zmiennej line powinna
zostać zwolniona. Wywołaniem funkcji
SLrli-
ne_close
zamykamy proces edycji (pomijamy
wywołanie funkcji
done_slang_readline
).
znaków, jak również znajdujemy współrzęd-
ne środka ekranu. Następnie za pomocą funkcji
SLsmg_fi ll_region
wypełniamy pewien obszar
ekranu jednolitym kolorem, aby następną funk-
cją narysować prostokąt. Wewnątrz tego prosto-
kąta umieszczamy komunikat tekstowy. Rysuje-
my również dwa dodatkowe przyciski -
OK
oraz
CANCEL
. Po tych przygotowaniach możemy
stosując funkcję Select pozwolić użytkowniko-
wi na wybór, czy chce skasować plik wybierając
OK
, czy też woli wycofać się z tej operacji po-
przez wybór przycisku
CANCEL
. Jeśli użytkow-
nik wybierze OK funkcja
recursive_del
usu-
nie wskazany plik bądź katalog. Jeśli nie mamy
praw dostępu, to w zmiennej le znajdzie się war-
tość -1. Możemy w ten sposób poinformować
użytkownika o braku praw dostępu.
Readline – obieranie
informacji od użytkownika
Podczas tworzenia katalogu, przenoszenia pli-
ku oraz kopiowania, użytkownik ma możliwość
podania nazwy nowego katalogu lub zmianę na-
zwy kopiowanego pliku. Edycja jest możliwa
dzięki zastosowaniu edytora liniowego. Biblio-
teka
S-Lang
daje możliwość skorzystania z pro-
stego edytora liniowego o nazwie
Readline
. W
naszym programie występują trzy funkcje:
in-
it_slang_readline
zajmująca się przygotowa-
niem odpowiednich zmiennych, druga
ReadLi-
neText
, która zgodnie z nazwą odczytuje linię
tekstu oraz
done_slang_readline
zamykają-
ca proces odczytu linii tekstu. Wszystkie naj-
ważniejsze czynności są wykonywane w funk-
cji
ReadLineText
. Jej kod źródłowy prezentu-
je Listing 4. Istotne są argumenty tej funkcji.
Dwa pierwsze,
x
oraz
y
, określają współrzędne
na ekranie, w którym będzie znajdować się po-
le edycyjne. Szerokość tego pola jest określona
Podsumowanie
Nie sposób opisać wszystkich detali związa-
nych z naszym programem. Choć ograniczyli-
śmy liczbę możliwości, to jak widać nie jest to
już program do napisania w przysłowiowy jeden
wieczór. Jednakże w tym, bądź co bądź, niewiel-
kim programie udało się zaimplementować pod-
stawowe opcje, takie jak kopiowanie i usuwanie
plików oraz katalogów. Swobodnie możemy się
także poruszać po strukturze katalogowej. Pro-
gram oferuje także możliwość zmiany podsta-
wowych atrybutów plików i katalogów. Wielką
zaletą naszego programu jest też jego wielkość.
Nasz program to zaledwie kilkadziesiąt kilobaj-
tów, choć wymagana jest dodatkowa bibliote-
ka
S-Lang
. Jednakże, jak zostało to pokazane,
w dość prosty sposób możemy utworzyć wersję
statyczną naszego programu. Jej zaletą jest nie-
zależność od zainstalowanych bibliotek.
W naszym programie brakuje także kilku
ważnych opcji. Wypadałoby zaimplementować
podgląd plików w postaci tekstowej. Oblicza-
nie wielkości zajętego miejsca przez wskazany
katalog także byłoby bardzo przydatną funkcją.
Nieco bardziej skomplikowaną funkcją były-
by dostęp do konsoli.Jednakże ta funkcja spo-
wodowałaby, iż nasz program zyskałby bar-
dzo wiele na funkcjonalności. Wiele większych
oraz mniejszych funkcji można dodać do opisy-
wanego programu, toteż zachęcam do wprowa-
dzania zmian i eksperymentowania z nim.
Okno kasowania pliku bądź katalogu
Listing 5. zawiera całą funkcję odpowie-
dzialną za narysowanie okna dialogowego z za-
pytaniem, czy skasować wskazany przez użyt-
kownika plik bądź katalog. Przed narysowaniem
okna należy uzyskać kilka istotnych dla nas in-
formacji. Obliczamy długość ścieżki (wielkość
ta zostaje zapisana w zmiennej l), dodając osiem
Wersja statyczna programu
Kompilacja omawianego programu przebiega w typowy sposób. Trzeba pamiętać, aby w
systemie dostępna była biblioteka
S-Lang
. Poszczególne pliki programu możemy kompilo-
wać w następujący sposób (zakładamy, że pliki źródłowe znajdują się w podkatalogu
src
):
gcc -c ./src/fs_oper.c -I./src
Budowa wersji binarnej programu, sprowadza się do wydania polecenia:
gcc -o minicmd dialogs.o fList.o fs_oper.o mainfi le.o workscr.o -lslang
Jednak tak powstały program będzie wymagał w systemie obecności biblioteki
S-Lang
. W
dość prosty sposób możemy usunąć to ograniczenie, tworząc program z użyciem biblio-
tek statycznych. Jednakże oprócz biblioteki
S-Lang
trzeba także dołączyć bibliotekę
Ncur-
ses
, w następujący sposób:
O autorze
gcc -o minicmd_static dialogs.o fList.o fs_oper.o mainfi le.o workscr.o /usr/
lib/libslang.a /usr/lib/libncurses.a
Autor zajmuje się tworzeniem oprogramo-
wania dla WIN32 i Linuksa. Zainteresowa-
nia: teoria języków programowania oraz
dobra literatura.
Kontakt z autorem:
autorzy@linux.com.pl
.
Uzyskana wersja binarna jest znacznie większa niż wersja niestatyczna, ale program dzia-
ła poprawnie w systemach, gdzie nie ma zainstalowanej biblioteki
S-Lang
.
www.lpmagazine.org
59
Plik z chomika:
SOLARIX33
Inne pliki z tego folderu:
2006.01_Koder plików w formacie OGG_[Programowanie].pdf
(722 KB)
2007.06_Piękno fraktali_[Programowanie].pdf
(1778 KB)
2008.11_GanttProject_[Programowanie].pdf
(1014 KB)
2007.04_USB Device Explorer_[Programowanie].pdf
(1134 KB)
2006.09_QT, PyQT – szybkie tworzenie baz danych_[Programowanie].pdf
(1319 KB)
Inne foldery tego chomika:
Administracja
Aktualnosci
Audio
Bazy Danych
Bezpieczenstwo
Zgłoś jeśli
naruszono regulamin