Árvore de páginas

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.

...

  • Disponivel em build superior à 131217A131227A.

  • A comunicação entre o SmartClient e o Navegador é feita através do componente TWebSocket, conforme exemplo a seguir.

 

Aviso
icontrue
titleNota 1

Nas plataformas móveis não é possível sobrepor componentes ADVPL sobre o Navegador do TWenEngine, esta é uma característica da biblioteca gráfica Qt.

Informações
icontrue
titleVeja também

BeginContent...EndContent

Exemplo completo

  • partir da versão 20.3.2.0 do SmartClient, o componente tem uma deficiência em sistemas operacionais Linux que ao exibir um PDF não é possível realizar o download e impressão dele pelos botões na barra superior. Esta deficiência é ocasionada por uma biblioteca de terceiros que estamos procurando solucionar.
Nota
titleNota sobre o uso de SessionStorage

Cada instância do componente TWebEngine no SmartClient Desktop se comporta como uma aba diferente de um web browser, possuindo cada uma sua própria SessionStorage.

Por outro lado, no SmartClient Webapp por naturalmente já ser executado em um web browser, suas instâncias de TWebEngine se comportam como Iframes e compartilham a mesma SessionStorage.

Nota
icontrue
titleImportante

É necessário baixar o arquivo totvstec.js para execução do teste.

Informações
icontrue
titleUtilizando o exemplo

1. Baixe e salve os conteúdos dos arquivos:
totvstec.js
webengine.prw
webengine.index.html

2. Compile os três arquivos através do TDS - Totvs Development Studio.

3. Execute a função u_webengine

webengine.prw

Bloco de código
languagecpp
themeEclipse
linenumberstrue
collapsefalse
#include "TOTVS.CH"
user function webengine()
	local i
	local cMultGet := ""
	local nPort,lConnected, link
	local aFiles := {"totvstec.js", "webengine.index.html"}
	local _tempPath := GetTempPath()
	PRIVATE oWebChannel
	PRIVATE oWebEngine
	PRIVATE oMultGet

	PRIVATE oDlg := TWindow():New(10, 10, 800, 600, "Exemplo", NIL, NIL, NIL, NIL, NIL, NIL, NIL,;
	                CLR_BLACK, CLR_WHITE, NIL, NIL, NIL, NIL, NIL, NIL, .T. )

	// Baixa arquivos do exemplo do RPO no diretorio temporario
	for i := 1 to len(aFiles)
		cFile := _tempPath + aFiles[i]
		nHandle := fCreate(cFile)
		fWrite(nHandle, getApoRes(aFiles[i]))
		fClose(nHandle)
	next i

	// ------------------------------------------
	// Prepara o conector WebSocket
	// ------------------------------------------
	oWebChannel := TWebChannel():New()
	nPort  		:= oWebChannel::connect() // Efetua conexão e retorna a porta do WebSocket
	lConnected 	:= oWebChannel:lConnected // Conectado ? [.T. ou .F.]

	// Verifica conexão
	if !lConnected
		msgStop("Erro na conexão com o WebSocket")
		return // Aborta aplicação
	endif

	// ------------------------------------------
	// Define o CallBack JavaScript
	// IMPORTANTE: Este é o canal de comunicação 
	//             "vindo do Javascript para o ADVPL"       
	// ------------------------------------------
	oWebChannel:bJsToAdvpl := {|self,codeType,codeContent| jsToAdvpl(self,codeType,codeContent) }

	// Monta link para navegação local inserindo "file:///"
	if subs(_tempPath,1,2) == "C:"
		_tempPath := "file:///" + strTran(_tempPath, "\", "/")
	endif
	link := _tempPath + "webengine.index.html"

	// ------------------------------------------
	// Cria navegador embedado
	// ------------------------------------------
	oWebEngine := TWebEngine():New(oDlg, 0, 0, 100, 100,, nPort)
	oWebEngine:navigate(link)
	oWebEngine:Align := CONTROL_ALIGN_ALLCLIENT

	// Painel inferior
	@ 038, 000 MSPANEL ConOut SIZE 250, 030 OF oDlg COLORS 0, 16777215 RAISED
	ConOut:Align := CONTROL_ALIGN_BOTTOM
	oMultGet := TSimpleEditor():New( 0,0,ConOut, 100,100,"",, {| u | if( pCount() > 0, cMultGet := u, cMultGet )}, , .T.)
	oMultGet:Align := CONTROL_ALIGN_ALLCLIENT
	
	// ------------------------------------------
	// Botão fara o disparo do método runJavaScript
	// que permite a execução via ADVPL de uma função JavaScript
	// ------------------------------------------
	@ 000, 204 BUTTON oButton1 PROMPT "runJavaScript" SIZE 045, 041 OF ConOut;
	     ACTION {|| oWebEngine:runJavaScript("alert('Alert Javascript\ndisparado via ADVPL')") } PIXEL
	oButton1:Align := CONTROL_ALIGN_RIGHT

	oDlg:Activate("MAXIMIZED")
Return

// ------------------------------------------
// Esta função recebera todas as chamadas vindas do Javascript
// através do método dialog.jsToAdvpl(), exemplo:
// dialog.jsToAdvpl("page_started", "Pagina inicializada");
// ------------------------------------------
static function jsToAdvpl(self,codeType,codeContent)
	local i
	local cFunJS
	local oTmpHashProg:= .F.
	local cCommand := ""

	if valType(codeType) == "C"
		_conout("jsToAdvpl->codeType: " + codeType + " = " + codeContent)

		// ------------------------------------------b
		// Recebe mensagem de termino da carga da página/componente
		// ------------------------------------------
		if codeType == "page_started"

			// ------------------------------------------
			// Ao terminar a carga da página inserimos um botão na página HTML
			// que fará a execução de uma função ADVPL via JavaScript
			//
			// Importante: O comando BeginContent permite a criação de pequenos trechos de código
			// facilitando a construção de chamadas, como neste exemplo onde montamos um trecho Javascript
			// ------------------------------------------
			BeginContent var cFunJS
				<a onclick='totvstec.runAdvpl("DtoS(CtoD(\"" +getDate()+ "\"))", runAdvplSuccess);'>
					<div>
						<font size="5">runAdvpl</font><br><br>
					</div>
				</a>
			EndContent

			// ------------------------------------------
			// O método AdvplToJS envia uma mensagem do ADVPL para o JavaScript, para verificar a chegada 
			// desta mensagem procure o trecho a seguir no arquivo webengine.index.html
			// if (codeType == "html") {...
			// ------------------------------------------
			oWebChannel:advplToJs("html", cFunJS)

		endif

		// ------------------------------------------
		// Este trecho vai executar um comando ADVPL vindo do JavaScript
		// ------------------------------------------
		if codeType == "runAdvpl"

			// ------------------------------------------
			// Importante:
			// A informação trafegada pela chamada do metodo Javascript runADVPL é uma variável do tipo JSON
			// assim é necessário seu tratamento, para tanto utilise as funções getJsonHash e getHField, documentadas neste exemplo
			// ------------------------------------------
			if getJsonHash(codeContent, @oTmpHashProg)
			
				cCommand := getHField(oTmpHashProg, "codeBlock") 
				if !empty(cCommand) 
				
					// Transforma o texo em um bloco de código e 
					// em caso de sucesso executa o mesmo 
					xVal := &("{|| " + cCommand + "}")
					if valType(xVal) == "B"
						xRet := eval(xVal)
						xRet := cValToChar(xRet) // Converte pra String
		
						// ------------------------------------------
						// Importante:
						// Este trecho executa do callBack (mensagem de retorno) para o Javascript
						// permitindo o retorno de informações ao HTML após o processamento via ADVPL
						// ------------------------------------------
						fnCallBack = getHField(oTmpHashProg, "callBack")+"('" +xRet+ "')"
						oWebEngine:runJavaScript(fnCallBack)
						
					endif
					
				endif
			endif
		
		endif

	endif

return

/* ---------------------------------------------------------------
 Parseia o Json em um Hash
 
 getJsonHash: recebe o conteudo no formato Texto que será parseado
 oHash: é uma varivel que receberá "por referencia" o conteudo HASH
        contido no Texto inicial, por ser uma variavel utilizada por referencia
        ela deve ter seu valor declarado anteriormente, exemplo: oTmpHashProg:= .F.
---------------------------------------------------------------*/
Static Function getJsonHash(jsonData, oHash)
	Local oJson := tJsonParser():New()
	Local jsonfields := {}
	Local nRetParser := 0

	if empty(jsonData)
		return .F.
	endif

	// Converte JSON pra Hash
	return oJson:Json_Hash(jsonData, len(jsonData), @jsonfields, @nRetParser, @oHash)
return

/* ---------------------------------------------------------------
 Retorna valor do campo no Hash
 
 oHash: variável no formato HASH criada pela função  getJsonHash
 jsonField: nome do campono no formato Texto que se deseja recuperar
 
 retorno: valor do campo encontrado ou "vazio" caso contrário
 ---------------------------------------------------------------*/
Static Function getHField(oHash, jsonField)
	Local xGet := Nil
	// Recupera valor do campo
	if HMGet(oHash, jsonField, xGet)
		return xGet
	else
		return ""
	endif
return

// ---------------------------------------------------------------
// Exibe conout no MultiGet para auxiliar na visualização em tela
// ---------------------------------------------------------------
static function _conout(cText)
	conOut(cText)
	oMultGet:Load(cText)
Return

webengine.index.html

Bloco de código
languagehtml
themeEclipse
linenumberstrue
collapsefalse
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <style>
        body { font-family: 'Calibri'; }
        a { text-decoration: none; }
        div { display: inline-block; float: left; background-color: #019AC4; color: #FFFFFF; 
              width: 250px; height: 34px; margin: 10px; padding: 10px; }
    </style>

    <script src="totvstec.js"></script>
    <script>
        onload = function () { loadEngine(); }

        function loadEngine() {
            // Conecta WebSocket Server
            totvstec.connectWS( function(){
                // Carrega mensageria exclusiva da pagina
                dialog.advplToJs.connect(function (codeType, codeContent) {
                    if (codeType == "html") {
                    	var page = document.getElementById("main");
                    	page.innerHTML += codeContent; 
                    }
                });

                // Envia sinal informando termino da carga da pagina
                dialog.jsToAdvpl("page_started", "Pagina inicializada");
            });            
        }

		function getDate(){
			var today = new Date();
			var dd = today.getDate();
			var mm = today.getMonth()+1; //January is 0!
			var yyyy = today.getFullYear();
			return mm+'/'+dd+'/'+yyyy;
		}
		
		function runAdvplSuccess(retStr){
			alert("CallBack da função runADVPL: " + retStr);
		}
    </script>
</head>

<body>
    <font face="calibri" color="#FFFFFF">
    <p id="main"></p>
</body>
</html>

Preview

Image Removed

Image Removed

Depurar a camada HTML/JS do WebEngine

É possível depurar a camada HTML/JS do componente TWebEngine invocando o SmartClient via linha de comando, como no exemplo abaixo:

smartclient --remote-debugging-port=8888

Onde a porta, no exemplo 8888, deverá ser chamada no navegador Chrome, permitindo então a depuração.

http://localhost:8888

Mais detalhes acesse o link abaixo:
Qt WebEngine Debugging and Profiling

A partir da build 20.3.0.0 é possível habilitar um parâmetro no arquivo smartclient.ini que exibe um console JavaScript.
JavaScriptConsole

A partir da build 20.3.2.0 é necessário informar também quem pode realizar o debug da camada HTML/JS via linha de comando:
smartclient --remote-debugging-port=8888  --remote-allow-origins=*

Informações
icontrue
titleVeja também

BeginContent...EndContent

Exemplo 

Nota

Abaixo esta disponível um exemplo documentado de uso do componente e o codigo JavaScript de apoio (twebchannel.js):
https://github.com/totvs/twebengine-sample
https://github.com/totvs/twebchannel-js

Visualização do exemplo

Exemplo TWebEngineImage Added

O TWebEngine no Youtube

Conector de Widget
urlhttp://youtube.com/watch?v=W2dXcaNLIxk
Image Removed

Abrangência

Protheus 11 , TOTVS Application Server