Képnézegető fejlesztése IV.

 

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!

Pammer Pál - pp@freeweb.hu