A napokban éppen a winchesterem formázása előtt álltam, és le szerettem volna menteni az értékes dolgokat róla, amikor igen nagy szolgálatot tett a példaprogramom! Rengeteg kép van a gépemen, és ezek közül kellett kiválogatnom azokat, amiket el kívántam menteni. Elsőre az ACDSee programmal próbálkoztam! Megjelenítettem a képeket, és amire nem volt szükségem, azt ki akartam törölni. Ment is a dolog egy darabig, de néhány perc után teljesen lemerevedett a gépem, csak „hardware resettel” lehetett kihozni a merev állapotából! Ezt még eljátszotta velem a program vagy ötször, aztán feladtam! Gondoltam jó lesz tesztfeladatnak a rendrakás, úgyhogy a példa programunkkal kezdtem a felesleges képeket kiválogatni. Legnagyobb megelégedésemre hiba nélkül végezte a dolgát, ráadásul egész gyorsan, hiszen a DEL billentyű leütésére már ki is törölte a felesleget állományt!
A munka végén vettem észre, hogy rengeteg kép van BMP formátumban, tehát rengeteg helyet foglalva, célszerű lenne ezeket átalakítani például JPG-be, de egyenlőre ezt nem tudtam a programmal megcsinálni!
Ezzel már meg is volt a következő fejlesztés irányvonala!
Azért nem ugrottam fejest a dologba, hanem először megpróbáltam végiggondolni, hogy mit és hogyan lenne célszerű elkészíteni! Én nem csak „sima” mentést akartam megvalósítani, hanem valamiféle „kötegelt” feldolgozást, amikor több állományon egyszerre végezhetem el ugyan azokat a műveleteket. Először is azt kellett eldönteni, hogy milyen műveleteket szeretnék elvégezni egy vagy akár több állományon:
Sima fájlok átmásolása másik alkönyvtárba
Színek felbontásának változtatása, növelése vagy csökkentése
Internethez „bemutató kicsi vagy nagy képek” gyors előállítása (méret változtatása)
Kép formátumok egymásba történő konverziója (BMP->JPG, -)
Ezek közül bármelyiket, vagy az összes műveletet végre lehessen hajtani!
Talán kezdjük is el, hiszen már tudjuk mit akarunk! Legyen egy mentés gomb, amivel a „hagyományos” mentést végezhetjük, és egy „Mentés másként” gombot, amivel a kötegelt feldolgozást indíthatjuk el!
A „speciális” kötegelt feldolgozáshoz valahol meg kell adnunk a feldolgozás paramétereit. Ez normál esetben egy dialógus ablakban történne, de mi nem „normális” programot írunk, hanem „profi”-t, tehát mi nem így csináljuk.
Felteszünk a felső ToolBar alá még egy ToolBar-t, és ebbe tesszük a beállító komponenseket. Ezzel azt érjük el, hogy könnyen módosíthatjuk a beállításokat, viszont helyet foglal az amúgy sem nagy ablakban, tehát lehetőséget adunk az ablak eltüntetésére is. Ezt a gombot a felső, mindig látható ToolBar-ra tesszük.
Készítsük el a paraméterek beállító ToolBar-t. Először az output alkönyvtárat, utána a mentés színmélységét adhatjuk meg, majd a mentés méretét és kiterjesztését. A méret és a kiterjesztés előtt CheckBox van, hogy ki/be tudjuk kapcsolni a paramétereket! Ezek elkészítése remélem senkinek sem okoz gondot!
A színmélység beállításánál alkalmazunk egy kis trükköt. A gombhoz egy PopupMenüt rendelünk, onnan válaszhatjuk ki az aktuális színmélységet. A kiválasztó gomb képe mindig az aktuálisan kiválasztott színt mutatja, és a Hint-jében pedig szövegesen írjuk az éppen aktuális színmélységet. Minden menüpontnak beállítjuk a Tag property-jét is, innen kapjuk majd meg a PixelFormat értéket. Minden menüponthoz ugyan azt az eventet rendeljük hozzá:
procedure TForm1.ColorOkClick(Sender: TObject);
var TM: TMenuItem;
i: integer;
s: string;
begin
// Régi elem törlése
for i:=0 to PopupMenu3.Items.Count-1 do
PopupMenu3.Items[i].Checked:=False;
// Aktuális menüelem kiválasztása
TM:=TMenuItem(Sender);
TM.Checked:=True;
ToolButton12.ImageIndex:=TM.ImageIndex; // Kép megjelenítése a gombon
ToolButton12.Tag:=TM.Tag; // A TAG tartalmazza a konverziót
s:=TM.Caption; // A menü szövegéből állítjuk elő a hintet
i:=Pos('&',s);
if i>-1 then
s:=Copy(s,1,i-1)+Copy(s,i+1,255);
ToolButton12.Hint:='Színmélység: '+s; // A megfelelő szöveget írja ki!!
SaveAsHint; // HINT beállítása
end;
Azért a trükköknek még nincs vége. Vagyunk olyan bátrak, hogy a beállításoknak megfelelően a „Mentés másként” gomb Hint szövegében meg is jelenjenek a beállított paraméterek, így a beállító ToolBar megnyitása nélkül is könnyen megtudhatjuk a paramétereket. A Hint generálását a SaveAsHint függvényben valósítjuk meg:
procedure TForm1.SaveAsHint;
var s: string;
begin
// Mentés másként csoport hintjének beállítása!!
s:='Mentés másként';
s:=s+#13+'Alkönyvár: '+Edit2.Text;
s:=s+#13+ToolButton12.Hint; // Színmélység!
if CheckBox2.Checked then // Méret!
s:=s+#13+'Méret: '+Edit4.Text+' x '+Edit5.Text;
if CheckBox1.Checked then // Kiterjesztés!!
s:=s+#13+'Kiterjesztés: '+ComboBox1.Items[ComboBox1.ItemIndex];
ToolButton10.Hint:=s;
// SAVE GOMB HINTJÉNEK BEÁLLÍTÁSA
if SavePictureDialog1.InitialDir<>'' then
ToolButton9.Hint:='Mentés - '+SavePictureDialog1.InitialDir
else
ToolButton9.Hint:='Mentés';
end;
A trükk tulajdonképpen igen egyszerű, a hint képes több soros szövegek megjelenítésére is, ha a szövegben #13 karaktert talál, tehát nekünk nincs más dolgunk, mint így előállítani a Hint szövegét!
A Save gomb hintjébe pedig az aktuális mentés alkönyvtárát tesszük bele, de erről majd később!
Természetesen minden beállító paraméter megváltoztatásakor a Hint-et frissítenünk kell! Azért ez sem olyan bonyolult, hiszen például az alkönyvtár Edit2 OnChange eventjénél semmi más dolgunk nincs, mint meghívni a SaveAsHint függvényt, és ezt az eventet ráállítani az összes paraméter megfelelő eventjére! Ugye nem is olyan bonyolult? Aki mégsem érti, az nézze meg a forrást, onnan minden kiderül!
Készítsük még el a mentés gombot, illetve a hozzá tartozó függvényt. A „Mentés” gomb lenyomásakor egy hagyományos SavePictureDialog dialógus ablakot használunk. A FileName-be betesszük az aktuálisan megjelenített fájl nevét, hátha másik alkönyvtárba kívánjuk csak átmásolni, majd meghívjuk a dialógus ablakot. Amennyiben volt kiválasztott fájl, elmentjük az aktuális alkönyvtár nevét, hátha legközelebb is ide kívánunk menteni, és ezt be is írjuk a mentés gomb hintjébe, majd meghívjuk a tényleges mentést:
procedure TForm1.ToolButton9Click(Sender: TObject);
begin
if FileListBox1.ItemIndex>-1 then
begin
// Adjuk át a fájl nevét!
SavePictureDialog1.FileName:=ExtractFileName(FileListBox1.FileName);
if SavePictureDialog1.Execute then
begin
SavePictureDialog1.InitialDir:=ExtractFilePath(SavePictureDialog1.FileName);
SaveAsHint; // Mentés Hintjének beállítása
Save(FileListBox1.FileName,SavePictureDialog1.FileName,False);
end;
end;
end;
A mentést a Save függvény végzi. Első két paramétere a forrás illetve cél fájl neve, a harmadik logikai érték azt mondja meg, hogy figyelembe kell e venni a kötegelt feldolgozás paramétereit.
A Save függvényben még csak a sima fájl másolást készítettem el:
procedure TForm1.Save(SourceFileName, DestFileName: string; Modi: Boolean);
var S_Ext, D_Ext: string;
begin
// Mentés végrehajtása
// Kiterjesztések leszedése
s_Ext:=UpperCase(ExtractFileExt(SourceFileName)); d_Ext:=UpperCase(ExtractFileExt(DestFileName));
if not Modi then // Sima mentés kell!!
begin
if s_Ext=d_ext then // Sima másolás
FileCopy(SourceFileName,DestFileName);
end;
end;
A másolást a FileCopy függvény oldja meg, tömören és gyorsan TFileStream objektummal:
Procedure FileCopy( Const sourcefilename, targetfilename: String );
Var
S, T: TFileStream;
Begin
S := TFileStream.Create( sourcefilename, fmOpenRead );
try
T := TFileStream.Create( targetfilename, fmOpenWrite or fmCreate );
try
T.CopyFrom(S, S.Size ) ;
finally
T.Free;
end;
finally
S.Free;
end;
End;
Mi értelme volt ennek a „kavarásnak”? Miért nem mentünk simán a „Mentés” gomb OnClick eventjében, és miért kell a gomb hintjébe beírni az aktuális alkönyvtár nevét? Nagyon egyszerű a magyarázata a dolgoknak. Képzeljük el, hogy képeket válogatunk, és a kiválasztott képet át kívánjuk másolni egy másik, de mindig azonos alkönyvtárba! Elég fárasztó és idegesítő a mindig megnyíló mentés dialógus ablak „Mentés” gombjára kattintani, amikor minden fájlt ugyan abba az alkönyvtárba kívánunk másolni. Alkalmazzuk erre azt a trükköt, hogy a kiválasztott fájlt vagy inkább fájlokat rádobjuk a mentés gombra, és az elvégzi a feladatát mindenféle kérdezgetés nélkül! Ezt hívják „Drag&Drop” vagyis „húzd és ejts” műveletnek. Nem gyorsabb és kellemesebb ez így? Ennek ellenére mégsem nagyon találkozunk hasonló megoldással!
Mi most elkészítjük ezt! Először is a FileListBox MultiSelect property-jét állítsuk True-ra, így lehetőségünk lesz arra, hogy több fájlt is kijelölhessünk egyszerre, erre a másolásnál és a csoportos műveleteknél is szükség lesz. Sajnos ennek átállítása új galibát okoz a programunkban! Például a fájl törlésekor vagy a bemutató használatakor a FileListBox kijelölése nem jól működik! Vajon mi lehet a gond? Sajnos nekem is ráment néhány órám, amíg megfejtettem a dolgot, pedig elég logikus a megoldás! MultiSelect esetén a FileListBox-nak a Selected[] propertyjén keresztül állíthatjuk be a kiválasztott fájl nevet. Ennek megfelelően módosítottam a FileDelete menüt, és a Timer1Timer eventet!
A kis kitérő és hibajavítás után térjünk vissza a Drag&Drop művelet megvalósítására! A FileListBox-nak a DragMode property-jét dmAutomatic-ra állítsuk, hogy a kijelölt fájlokat el tudjuk mozdítani a mentés gomb felé. Ennek hatására a már kijelölt fájlokat „megfogva” és az egeret elhúzva megjelenik a „Drag&Drop” kurzor, ami azt mutatja, hogy a funkció működik.
Az innen elindított funkciót a mentés gombnak kell fogadnia! A Drag&Drop funkció megvalósításához a legegyszerűbb esetben két eventre van szükségünk a „fogadó” komponensen, jelen esetben a mentés gombon. Az OnDragOverrel szabályozhatjuk, hogy „kitől” fogadunk el adatokat, az OnDragDrop eventben pedig a tényleges „fogadást” kell megvalósítanunk.
Először is a mentés gomb OnDragOver event-ben megadjuk, hogy „kitől” fogadunk el Drag&Drop funkciót. Jelen esetben a FileListBox1-től:
procedure TForm1.ToolButton9DragOver(Sender, Source: TObject; X,
Y: Integer; State: TDragState; var Accept: Boolean);
begin
Accept:=(Source=FileListBox1);
end;
Ezzel meg is vagyunk, már csak a tényleges fogadást, az OnDragDrop funkciót kell megvalósítanunk, de az sem bonyolult, hiszen már minden meg van hozzá:
procedure TForm1.ToolButton9DragDrop(Sender, Source: TObject; X,
Y: Integer);
var s: string;
i: integer;
begin
if (Source=FileListBox1) and (SavePictureDialog1.InitialDir<>'')
and (SavePictureDialog1.InitialDir<>DirectoryListBox1.Directory)then
begin
Screen.Cursor:=crHourglass;
for i:=0 to FileListBox1.Items.Count-1 do
begin
if FileListBox1.Selected[i] then // Ki van választva, másolni kell!!
begin
// Cél nevének előállítása
s:=SavePictureDialog1.InitialDir+'\'+ExtractFileName(FileListBox1.Items[i]);
// Tényleges mentés!!
Save(DirectoryListBox1.Directory+'\'+FileListBox1.Items[i],s,False);
end;
end;
Screen.Cursor:=crDefault;
end;
end;
Megvizsgáljuk, hogy a cél alkönyvtár meg van e adva, és ez nem egyezik e meg az aktuális alkönyvtárral, és ezután a kiválasztott fájlokat szépen átmásoljuk a Save függvénnyel! Nem is volt bonyolult, igaz? Azért van még egy kis szépséghibája a dolognak. Jó lenne valamiből látni, hogy csinál valamit a program, és nem csak „elájult”, de ezt majd legközelebb!
Most már csak a szokásos dolgok vannak hátra: a beállított paraméterek, a SavePicturDialog alkönyvtárának, gombok állapotának elmentése, visszaállítása, de ez már mindenkinek megy, tehát le sem kell írnom!
A következő részben a „kötegelt”, mentés másként funkciót fogjuk megoldani, és akkor már le is köröztünk néhány „profi” programot!
A forrás a lefordított programmal megtalálható a CD-n!