Jak w Delphi (TWebBrowser) czekać na zakończenie wyświetlania się mapy Google?

Odpowiedz Nowy wątek
2015-04-06 09:36
Zimny Mleczarz
0

Otóż piszę program w Delphi, który musi pobrać mapę Gogle w postaci JPG. Wymyśliłem sobie, że będzie pobierał tą mapę jako zrzut ekranu, a w zasadzie zrzut tego co wyświetla IE (z komponentu TWebBrowser). Ale od początku…

Delphi korzysta z takiego komponentu o nazwie TWebBrowser. Ten komponent to jest w zasadzie IE. No i znalazłem w Internecie fragmenty skryptów JavaScript (nie znam tego języka kompletnie), które sobie poskładałem w całość i w rezultacie zrobiłem stronę HTML, która zawiera pola INPUT do ręcznego wpisywania danych oraz do tego, aby Delphi jakoś mogło się komunikować z tą stroną HTML. Strona zawiera również JavaScript do pobierania mapy z Google.

Przykładowo, to się dzieje tak: Delphi odnajduje plik HTML na dysku. Bada czy przeglądarka (TWebBrowser) ma już załadowany ten plik. Jeśli nie ma, to Delphi ładuje do komponentu TWebBrowser plik HTML i oczekuje na jego załadowanie (właściwość „Busy” komponentu , działa dobrze). Po załadowaniu strony, Delphi uzupełnia w przeglądarce odpowiednie pole INPUT np.: „address” a następnie zdalnie „klika” w stronie HTML (pole type=”buton”) guzik „Ustaw po adresie”. Teraz Delphi znów musi czekać, aż kod JavaScript się zakończy i mapa zostanie pobrana z Google i wyświetlona w przeglądarce, aby następnie zrobić zrzut wyświetlanej zawartości do pliku. I tu mam problem z tym czasem oczekiwania. Wymyśliłem sobie, że wprowadzę kodzie Java-Script, w pliku HTML, ukryte pole INPUT (type=hidden), w którym ustawię na początku każdej procedury „NO” a jak się kod JavaS wykona, to ustawię „YES”. Delphi, po tym jak „kliknie” zdalnie w stronie HTML guzik „ustaw po adresie”, bada w szybkiej pętli wartość tego pola i oczekuje, aż przyjmie wartość „YES” i wtedy uznaje, że mapa została już pobrana i wyświetlona. Niestety – tu jest problem bo o ile kod JavaScript faktycznie się już zakończy to mapa wcale nie została jeszcze wyświetlona na ekranie przez przeglądarkę. W tym czasie przeglądarka (komponent) zdołał wyświetlić tylko widoczne 2 pola edycyjne INPUT oraz klawisze typu BUTTON i szare tło. Mapy w tym momencie jeszcze nie ma, przeglądarka jeszcze coś robi w tle i mapa ukazuje się za kilka milisekund (różny czas w zależności od szybkości komputera). Ale Delphi już dalej nie czeka – strzela zrzut ekranu do pliku i mam obrazek bez widocznej mapy, tylko z szarym tłem.

Pytanie i prośba. Jak to zrobić aby w kodzie JavaScript badać i czekać – kiedy przeglądarka faktycznie wyświetli mapę na ekranie? Czekać w jakieś pętli JavaS na moment, kiedy przeglądarka zakończy nie tylko:
a) ładowanie i przetwarzanie strony HTML (to mam opanowane),
b) wykonywanie kodu JavaScript (to też mam opanowane),
ale czekać, aż fizycznie wyświetli obraz mapy dla użytkownika. Jak poczekam w pętli JavaS to Delphi również będzie czekać na wykonanie JavaS i wszystko będzie git. Problem w tym, że kod JavaS się kończy, ustawia pole hidden na "YES" a obrazu nie ma jeszcze jakiś czas.

Dla jasności sprawy, oprogramowanie tego co jest podane w Internecie czyli :

var
WebCurDispatch: IDispatch;
WebDocLoaded: Boolean;

(…)
repeat
  Application.ProcessMessages;
 until GMapsWB.ReadyState = READYSTATE_COMPLETE; 
 repeat
  Application.ProcessMessages;
 until WebDocLoaded;
// (…)

// Oczekiwanie na TWebBrowcer - zakończenie ładowania strony

procedure TMainForm.GMapsWBBeforeNavigate2(ASender: TObject; const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData, Headers: OleVariant; var Cancel: WordBool);
begin
 WebCurDispatch := nil;
 WebDocLoaded := False;
end;

// Oczekiwanie na TWebBrowcer - zakończenie ładowania strony
procedure TMainForm.GMapsWBDocumentComplete(ASender: TObject; const pDisp: IDispatch; var URL: OleVariant);
begin
 if (pDisp = WebCurDispatch) then
 begin
  WebDocLoaded := True;
  WebCurDispatch := nil;
 end;
end;

nie rozwiązuje problemu. To znaczy, że IE twierdzi, że już wszystko jest wykonane ale obrazek mapy nadal nie jest widoczny na ekranie.

Komponent Delphi ma dużo zdarzeń ale takowego jak mi potrzebne to (chyba) nie ma. W każdym razie testowałem wszystkie. Mogę tylko badać, kiedy zakończy się ładowanie strony HTML, a to ma miejsce na długo przed wykonaniem kodu Java, więc potem czekam na zakończenie kodu Java i … tyle, nie wiem jak dalej, jak zbadać kiedy w końcu IE wyświetli obraz mapy dla użytkownika. Patrzyłem neta ale nie znalazłem nic ciekawego .

Nie chcę „sztywnych” spowalniaczy w stylu „Sleep(1000)” , gdzie z góry określam czas oczekiwania. Zależy mi na tym, aby przechwycić zdarzenie z przeglądarki na zasadzie, że IE mówi: „Zakończyłam wyświetlanie mapy na canvas, użytkownik widzi mapę na ekranie.”.

No chyba, że inny pomysł – mi przyszło do głowy, że to JavaS może zapisywać mapę Google do pliku JPG, a nazwę pliku z mapą wziąć z pola hidden (Delphi ustawi), tylko że ja nie umiem czegoś takiego napisać w JavaS, a za to wiem jak w Delphi zrobić zrzut ekranu do pliku JPG, no to wybrałem znane mi rozwiązanie.

Pytanie 2. Dlaczego w TWebBrowser lub w samym IE, ta (poniższa) strona HTML ładuje się masakrycznie wolno, a w FireFox śmiga jak Formuła 1 ? Wcześniej było dobrze (czyli szybko) w IE ale od lutego 2015 coś się zmieniło w IE i ten sam HTML teraz wykonuje się nie 3s tylko 20s.

<!DOCTYPE html>
<!-- saved from url=(0014)about:internet -->
<html>
  <head>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
    <meta http-equiv="Content-Language" content="pl">
    <meta http-equiv="Content-Type" content="text/html; charset=windows-1250">
    <title>Geocoding service</title>
    <style>
      html, body, #map-canvas {
        height: 100%;
        margin: 0px;
        padding: 0px
      }
      #panel {
        position: absolute;
        top: 5px;
        left: 20%;
        margin-left: 0px;
        z-index: 5;
        background-color: #fff;
        padding: 5px;
        border: 1px solid #999;
      }
    </style>

<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>

<script>
var geocoder;
var map;
var markersArray = [];
var infowindow = new google.maps.InfoWindow(); 

function initialize() {
  geocoder = new google.maps.Geocoder();
  var latlng = new google.maps.LatLng(52.173931692568, 18.8525390625);
  var mapOptions = {
    zoom: 8,
    center: latlng
  }
  map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
}

// Ta funkcja nie musi tu być, ja jej nie używam aczkolwiek można użyć, kiedy się stosuje wywołanie "window.onload = loadScript;"
function loadScript() {
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = 'https://maps.googleapis.com/maps/api/js?v=3.exp&signed_in=true&callback=initialize';
  document.body.appendChild(script);
}

// Czyszczenie wszystkich markerów na mapie
function clearOverlays() {
  for (var i = 0; i < markersArray.length; i++ ) {
    markersArray[i].setMap(null);
  }
  markersArray.length = 0;
}

//Rozkodowanie adresu na lokalizację na mapie i ustawienie markera na pozycji
function codeAddress() {
  document.getElementById('JavaDone').value = 'NO';
  var address = document.getElementById('address').value;
  geocoder.geocode( { 'address': address}, function(results, status) {
    if (status == google.maps.GeocoderStatus.OK) {
      map.setCenter(results[0].geometry.location);
      clearOverlays();
      var marker = new google.maps.Marker({
          map: map,
          position: results[0].geometry.location
        });
      markersArray.push(marker);
      map.setZoom(18);
    } else {
      alert('Geocode was not successful for the following reason: ' + status);
    }
  });
  document.getElementById('JavaDone').value = 'YES';
}

//Rozkodowanie współrzędnych GPS na lokalizację na mapie i ustawienie markera na pozycji
function codeLatLng() {
  document.getElementById('JavaDone').value = 'NO';
  var input = document.getElementById('latlng').value; 
  var latlngStr = input.split(',', 2); 
  var lat = parseFloat(latlngStr[0]); 
  var lng = parseFloat(latlngStr[1]); 
  var latlng = new google.maps.LatLng(lat, lng); 
  geocoder.geocode({'latLng': latlng}, function(results, status) { 
    if (status == google.maps.GeocoderStatus.OK) { 
      if (results[1]) { 
        map.setCenter(latlng);
        var marker = new google.maps.Marker({ 
            map: map,
            position: latlng
        }); 
        map.setZoom(18); 
        infowindow.setContent(results[1].formatted_address); 
        infowindow.open(map, marker);
        document.getElementById('address').value = results[1].formatted_address;
      } else {
        alert('No results found'); 
      } 
    } else { 
      alert('Geocoder failed due to: ' + status); 
    } 
  }); 
  document.getElementById('JavaDone').value = 'YES';    
}

 google.maps.event.addDomListener(window, 'load', initialize);
// window.onload = loadScript;
</script>

  </head>
  <body>
    <div id="panel">
      <input id="JavaDone" name="JavaDone" type="hidden" value="NO">

      <input id="address" name="address" size="60" type="textbox">
      <input id="addressButton" type="button" value="Adres" onclick="codeAddress()">
      <br>
      <input id="latlng" size="30" type="textbox"> 
      <input id="gpsButton" type="button" value="GPS" onclick="codeLatLng()">
    </div>
    <div id="map-canvas"></div>
  </body>
</html>
edytowany 2x, ostatnio: furious programming, 2016-12-13 18:26
Szanuj czytelników swoich postów. Wstawiaj kod w odpowiednie znaczniki. Poprawiłem. Nastepnym razem będzie Kosz. - olesio 2015-04-06 11:02
Dorzuciłem jeszcze wewnętrzne znaczniki dla CSS i JS, bo kolorowanie skopało się; - furious programming 2015-04-07 00:10

Pozostało 580 znaków

2015-04-07 08:17
0

Głupie to jak nie wiem u mnie nie zauważam problemu z opóźnionym ładowaniem mapy ale spróbuj może czekać aż w div pojawi się kod HTML mapy. Nie wiem czy to zadziała bo w zasadzie samo READYSTATE_COMPLETE teoretycznie powinno działać ale spróbuj coś w stylu.:

var
   Doc: IHTMLDocument3;
   Element: IHTMLElement;
begin
  WebBrowser1.Navigate('f:\test.htm');
  repeat
    Application.ProcessMessages;
  until WebBrowser1.ReadyState = READYSTATE_COMPLETE;
  Doc:= WebBrowser1.Document as IHTMLDocument3;
  if Assigned(Doc) then
  begin
    Element:= Doc.getElementById('map-canvas');
    if Assigned(Element) then
    begin
      repeat
        Application.ProcessMessages;
      until Length(Element.innerHTML) > 0;
      //zaladowany
    end;
  end;
end;

Nie odpowiadam na PW w sprawie pomocy programistycznej.
Pytania zadawaj na forum, bo:
od tego ono jest ;) | celowo nie zawracasz gitary | przeczyta to więcej osób a więc większe szanse że ktoś pomoże.

Pozostało 580 znaków

2015-04-07 17:00
0

a cos takiego by Ci nie pomoglo?

http://sourceforge.net/projects/gmlibrary/


Tomasz Andrzejewski
Delphi (XE3-XE7) framework engineer @ InterLan
MCP: Microsoft SQL Server 2008, Implementation and Maintenance

Pozostało 580 znaków

2015-04-09 12:14
Zimny Mleczarz
0

Efekt jest taki, ze strzela zrzut ekranu w momencie kiedy mapa jest już niemal wyświetlona ale jednak nie jest jeszcze do końca, zaraz zaraz jak na tym forum się wkleja obrazek...

a81da501f1.png

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0