Sortowanie zdjęć po dacie ostatniej modyfikacji (VBA i js)   strona główna:
A po co ten Excel ;-)
 
Po sporej przerwie... postaram się wrócić do aktywności również na tej stronie. Na początek zabawka na którą wpadłem po powrocie  
z weekendu majowego... Mianowicie.  
 
   Jesteśmy sobie na na wakacjach i robimy zdjęcia... Aparatem fotograficznym, drugim aparatem (np.: brata) i jeszcze komórką - bo   
zawsze pod ręką. :) Po powrocie zrzucamy wszystkie fory do jednego folderu i komuś pokazujemy. Fajnie by było żeby zdjęcia, bez  
względu na to z którego urządzenia pochodzą, były posortowane chronologicznie.  
   Jakoś nigdy do tej pory tego nie potrzebowałem ;) a że zamiast szukać istniejących opcji wolałem napisać to sobie sam to też   
pokażę jak na UserForm'ie przeglądać zdjęcia z określonego folderu w kolejności rosnącej po dacie ostatniej modyfikacji pliku za pomocą  
klawiszy strzałek.  
A że... Jakiś czas temu w prezencie dostałem książeczkę o JavaScript to teraz miewam pomysły typu: "a jak by to napisać w js ??"  
Będzie też więc przykład wykorzystania skryptu w tym języku do realizacji zadania. Jednak ciągle główną aplikacją będzie Excel :P  
I choć całość będzie w pliku htm to jednak całość będzie obsługiwana na kontrolce WebBrowser na UserForm'ie której jedynie wskażę  
gdzie jest pliki ze skryptem..  
                                     zachęcam do lekturki :D  
 
Wersja VBA  
w module Standardowym  
 
Public tblFoty As Variant, iTBL As Long  
 
Sub start()  
    UserForm1.Show  
End Sub  
 
Sub LoadTBL(strFolderName As String)  
    Dim tbl As Variant, jTbl  As Long  
      
    tbl = ListaZdjec(strFolderName)  
    If Not IsEmpty(tbl) Then  
        SortowanieBabelkowe2D_H tbl, 2  
        For jTbl = 1 To UBound(tbl, 2)  
            tblFoty = tbl  
        Next  
    End If  
End Sub  
 
Procedura LoadTBL będzie wywoływana przy UserForm_Initialize gdzie również podam ścieżkę do folderu ze zdjęciami. Do zmiennej tbl funkcją  
ListaZdjec zapiszę tablicę 2D ze ścieżkami do obrazów i datami ostatniej modyfikacji pliku.  
 
Function ListaZdjec(strFolderName As String) As Variant  
    Dim objFSO As Object 'Scripting.FileSystemObject  
    Dim objFolder As Object 'Scripting.Folder  
    Dim objFile As Object 'Scripting.File  
    Dim tblZdjecia() As Variant, iZ As Long  
      
      
    Set objFSO = CreateObject("Scripting.FileSystemObject")  
    Set objFolder = objFSO.GetFolder(strFolderName)  
      
    For Each objFile In objFolder.Files  
        With objFile  
            If UCase(.Type) Like "*OBRAZ*" Then  
                iZ = iZ + 1  
                ReDim Preserve tblZdjecia(1 To 2, 1 To iZ)  
                tblZdjecia(1, iZ) = .Path  
                tblZdjecia(2, iZ) = .DateLastModified  
            End If  
        End With  
    Next  
      
    If iZ > 0 Then ListaZdjec = tblZdjecia  
      
    Set objFSO = Nothing  
    Set objFolder = Nothing  
 
End Function  
 
   Jak widać do przeglądania plików w folderze i określania ich właściwości wykorzystuję obiekty biblioteki Scripting Runtime. W efekcie  
otrzymuję poziomą tablicę z potrzebnymi informacjami o każdym pliku. Tablica jest pozioma bo dynamicznie można poszerzać tylko   
ostatni jej wymiar. Jednak uznałem że bez sensu Transponować tę tablicę. Ścieżki można odczytać i z takiej. Jednak ścieżki mają być  
posortowane we daty ostatniej modyfikacji (wiersz 2 tablicy) będziemy więc potrzebować procedury która sortnie taką tablicę po   
wskazanym przez użytkownika wierszu/ach.  
 
Public Sub SortowanieBabelkowe2D_H(tabl, ParamArray nrWiersza() As Variant)  
 
    Dim nr As Variant  
    Dim xMax As Long, yMax As Integer, jTbl As Integer  
    Dim i As Long, j As Long, a As Long  
    Dim Temp  
 
    xMax = UBound(tabl, 1): yMax = UBound(tabl, 2)  
    For Each nr In nrWiersza  
        a = 2  
        For i = 2 To yMax  
            For j = yMax To a Step -1  
                If tabl(nr, j - 1) > tabl(nr, j) Then  
                    For jTbl = 1 To xMax  
                        Temp = tabl(jTbl, j - 1)  
                        tabl(jTbl, j - 1) = tabl(jTbl, j)  
                        tabl(jTbl, j) = Temp  
                    Next  
                End If  
            Next  
            a = a + 1  
        Next  
    Next  
End Sub  
 
   A więc mamy sprawę załatwioną w ramach zapisu do publicznej zmiennej tablicy zawierającej ścieżki do obrazów z danego folderu   
posortowane wg daty ich ostatniej modyfikacji.  
Pozostaje sprawa odczytu tej tablicy i załadowanie danego zdjęcia do kontrolki Image na UserForm'ie.  
 
W kodzie UserForm'a  
 
Option Explicit  
 
Private Sub UserForm_Initialize()  
    Const strFolderName As String = "D:\tkuchta1\prywatne\władysławowo"  
      
    LoadTBL strFolderName  
    iTBL = 1: LoadPicture iTBL  
End Sub  
 
Private Sub UserForm_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)  
    'http://msdn.microsoft.com/en-us/library/0z084th3(v=vs.80).aspx  
    'Key Code Constants for Visual Basic 6.0 Users  
 
    Const vbKeyEscape = 27  
    Const vbKeyLeft = 37  
    Const vbKeyRight = 39  
      
    Select Case KeyCode  
        Case vbKeyEscape:   Unload Me: Exit Sub  
        Case vbKeyRight:    iTBL = iTBL + 1  
        Case vbKeyLeft:     iTBL = iTBL - 1  
    End Select  
      
    If iTBL > 0 And iTBL <= UBound(tblFoty, 2) Then  
        LoadPicture iTBL  
    End If  
End Sub  
 
Private Sub UserForm_Terminate()  
    Erase tblFoty: iTBL = 0  
End Sub  
 
Sub LoadPicture(nr As Long)  
    Me.Image1.Picture = stdole.LoadPicture(tblFoty(1, nr))  
    Me.Label1.Caption = tblFoty(1, nr)  
    Me.Label2.Caption = Format(tblFoty(2, nr), "yyyy-mm-dd hh:mm:ss")  
End Sub  
 
Sprawa raczej jasna - nie :))  
 
 
Większa zabawa jest w wersji JavaScript :-)  
Nie wiem na ile optymalne jest prezentowane rozwiązanie gdyż jest to mój drugi program napisany w js więc doświadczenie  
Otwieramy notatnik piszemy poniższy kod i zapisujemy plik jako start.htm  
 
<body frameborder="no" scroll="no" topmargin="0" leftmargin="0">  
   <img id="myPicture" height="600" width="800">  
   <script type="text/javascript">  
 
    document.onkeydown = keyInfo;  
    window.onload = listazdjec;  
 
    var vFiles = new Array();   
    var nrFoty = 0  
 
    function listazdjec() {  
      var strFolderName = "D:\\tkuchta1\\prywatne\\władysławowo";  
 
      var objFSO = new ActiveXObject("Scripting.FileSystemObject");  
      var objFolder = objFSO.GetFolder(strFolderName);  
      var objFilesColl = new Enumerator(objFolder.files);  
 
      var arrfiles = new Array(); var i = 0;  
      var arrDataParts = new Array()  
 
      for (; !objFilesColl.atEnd(); objFilesColl.moveNext()) {  
 
        var objFile = objFSO.GetFile(objFilesColl.item());  
        var strType = objFile.Type;  
 
        if (strType.match(/obraz.*/i)) {  
          i++  
          var sLastMod = String(objFile.DateLastModified);  
          arrDataParts = sLastMod.split(" ");
          arrfiles[i] = arrDataParts[5] + monthNr(arrDataParts[1]) + format00(arrDataParts[2]) + arrDataParts[3] + "|" + objFile.Path 
        }
      }  
 
      if (i > 0) {  
        vFiles = arrfiles.sort();  
        showImage (0); nrFoty = 1  
      }  
    }  
        
    function monthNr(strMonthName) {  
      var arrMonthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]  
 
      for (var a=0;a<arrMonthNames.length;a++) {  
        if (arrMonthNames[a] == strMonthName) {  
          if (a < 9) {  
            return ("0" + String(a+1));  
          }  
          else {  
            return (a+1);  
          }  
        }  
      }  
    }  
 
    function format00(iNr) {  
 
      if (parseInt(iNr) < 10) {  
        return ("0" + iNr);  
      }  
      else {  
        return (iNr);  
      }  
    }  
 
    function showImage(nr) {  
      var strfileInfo = vFiles[nr];  
      var arrTblInfo = strfileInfo.split("|");  
   
      document.getElementById("myPicture").src = arrTblInfo[1]  
    }   
 
    function keyInfo() {  
      Key = event.keyCode;  
 
      if (Key == 37) {  
        if (nrFoty > 1) {  
          nrFoty--  
        }  
      }  
      if (Key == 39) {  
        if (nrFoty < vFiles.length) {  
          nrFoty++  
        }  
      }  
      showImage (nrFoty)  
    }  
 
   </script>  
</body>  
 
dla ciekawych (a przecież cała ta strona jest właśnie dla ciekawych).. We fragmentach... Pare słów co i jak..  
 
    document.onkeydown = keyInfo;  
    window.onload = listazdjec;  
 
To zdarzenia pod które będę podpinał funkcje. window.onload - uruchomi się tuż po załadowaniu strony - WebBrowser. document.onkeydown do   
ładowania kolejnego lub poprzedniego obrazka po kliknięciu na strzałkę w lewo lub w prawo..  
 
    var vFiles = new Array();   
    var nrFoty = 0  
 
To "publiczna tablica" do której trafią posortowane rosnąco ścieżki zdjęć z danego folderu. Zostanie ona utworzona po wykonaniu się   
funkcji podpiętych pod zdarzenie window.onload  
 
    function listazdjec() {  
 //...  
      var objFilesColl = new Enumerator(objFolder.files);  
 //...  
      for (; !objFilesColl.atEnd(); objFilesColl.moveNext()) {  
        var objFile = objFSO.GetFile(objFilesColl.item());  
 
Chcąc przeglądać wszystkie pliki w folderze przydała by się pętla For Each ... Next. Tu czegoś takiego nie ma (chyba) stąd potrzeba   
obiektu Enumerator'a. To coś jak kolekcja i dzięki jej właściwością .atEnd() i .moveNext() można wyłuskać obiekty z obiektu   
nadrzędnego którym w tym przypadku jest folder.  
 
        if (strType.match(/obraz.*/i)) {  
 
Tu nie ma operatora LIKE. :( Są jednak Wyrażenia Regularne... :)  
 
          var sLastMod = String(objFile.DateLastModified);  
          arrDataParts = sLastMod.split(" ");
          arrfiles[i] = arrDataParts[5] + monthNr(arrDataParts[1]) + format00(arrDataParts[2]) + arrDataParts[3] + "|" + objFile.Path 
Kolejnym problemem jest odczytanie daty z .DateLastModified danego pliku. Właściwość ta zwraca coś na kształt..  
Son May 7 13:54:17 UTC+0200 2011  
Wykombinowałem żeby to dzielić po spacji (" ") i połączyć odpowiednie fragmenty.  
Funkcje monthNr i format00 bardzo proste więc nie ma o czym pisać.  
 
Następny.. Miałem problem z obsługą dwuwymiarowej dynamicznej tablicy... trzeba było z niej zrezygnować :(  
Stanęło na utworzeniu dynamicznej jednowymiarowej tablicy do której wstawiam ciąg będący połączeniem daty, znaku 124 "|" i ścieżki.  
 
W efekcie tablica miała być wypełniania ciągiem np.:  
2011050713:54:17|D:\\tkuchta1\\prywatne\\władysławowo\\P20110507123.jpg  
Sortując takie ciągi alfabetycznie zostaną one posortowane prawidłowo :-))  
 
        vFiles = arrfiles.sort();  
 
No i miłe zaskoczenie.. Jest metoda .sort() sortująca tablicę :) Jednak sprawa z głowy.. :P  
 
    function showImage(nr) {  
      var strfileInfo = vFiles[nr];  
      var arrTblInfo = strfileInfo.split("|");  
   
      document.getElementById("myPicture").src = arrTblInfo[1]  
    }   
 
I meritum zabawy.. Tj odczyt danego elementu "publicznej tablicy" vFiles. Podział otrzymanego ciągu po znaku 124 "|" i załadowanie  
obrazu ze ścieżki.. Funkcję tą wywołuje się: po utworzeniu tablicy - w celu wyświetlenia pierwszego zdjęcia i po naciśnięciu strzałek  
w funkcji KeyInfo()..  
 
W kodzie UserForma trzeba jedynie dopisać..  
   
Private Sub UserForm_Initialize()   Przykład do pobrania
    With Me.WebBrowser1   sortfot.zip
        .Navigate ThisWorkbook.Path & "\start.htm"    
    End With  
End Sub  
 
i po zadaniu.. :-)