Lazarus fpCEF3 - Учебный проект 3 - веб-интерфейс для десктопного приложения
В третьем проекте мы откроем полноценную веб-страницу (локальную) с несложным веб-интерфейсом пользователя.
Мы запретим переход по ссылкам (смену URL-адреса) и новые объекты браузера (окна), организуем синхронизацию с веб-страницей.
Никаких привычных контролов и меню десктоп-приложения мы делать не будем, заменим их на веб-контролы, хотя вы можете при желании продублировать или разделить некоторые функции.
Я приготовил простую тестовую локальную веб-страницу для этого учебного проекта. При желании вы можете программно формировать и менять содержимое локального HTML-файла, перезагружая затем его в браузере (Browser.Reload).
Скачать HTML-страницу в zip-архиве (33 Kb)
Тестовый HTML-файл этого проекта, чтобы не было вопросов лицензирования, содержит 100% мой собственный код (включая JavaScript-модуль VcorpJS) и может содержать веб-аналоги:
- ShowMessage() ( модальное окно сообщения ),
- CheckBox ( переключатели-флажки checkbox ),
- ComboBox ( комбинированный список combobox ),
- модальное диалоговое окно с видео-роликом,
- анимационные меню для выбора фильма,
и т.д. и т.п. - который вы можете заменить на любой код и веб-фреймворк, с которым работаете.
Ничего не мешает работать с веб-страницами существующих сайтов и веб-систем, оптимизированных для работы с вашими десктопными клиентами - у таких веб-страниц больше прав по сравнению с локальными. Лучше не отключать в компоненте TChromium средства безопасности.
У нас должно получиться примерно следующее:
Скриншоты проекта в IDE и работающего приложения project3.exe




Для навигации по всем страницам этого раздела сайта используйте иконку меню или ссылку Навигация в строке навигации.
Для этого проекта нам понадобится подключение дополнительного модуля LCLProc (если версия Lazarus IDE 1.4.4) или модуля LazUTF8 (если версия Lazarus IDE 1.6.0 и выше) - для поиска подстрок функцией UTF8Pos() - чтобы определять, что нам сообщает JavaScript браузера.
Создадим одну форму (главную), в свойстве Constrains установим «MinHeight=480» и «MinWidth=640» (тестовая HTML-страница использует минимум 640х480 пикселей).
Разместим на форме единственный контрол-компонент TChromium, который растянем на всю форму:
Anchors: [akTop,akLeft,akRight,akBottom]
Сделаем обработку ошибки загрузки страницы в браузер «OnLoadError»
procedure TForm1.Chromium1LoadError(Sender: TObject;
const Browser: ICefBrowser; const Frame: ICefFrame;
errorCode: TCefErrorCode; const errorText, failedUrl: ustring);
var v: variant;
begin
v:=errorCode; // Ошибка загружки веб-страницы
ShowMessage('Ошибка загрузки страницы' + #13 + 'errorCode = ' + IntToStr(v) + ' ' +
UTF8Encode(errorText + #13 + failedUrl) + #13 + 'Попробуйте ещё раз');
end;
Создадим обработчик события «OnConsoleMessage» для синхронизации с нашей активной веб-страницей (HTML-файлом).
Наша тестовая веб-страница раз в секунду проверяет связь с нашим приложением, поэтому при каждом событии от консоли необходимо заново устанавливать значение переменной 'window.lz_cef3=1;' через вызов ExecuteJavaScript()
Наша тестовая HTML-страница начинает вывод в консоль с момента готовности DOM (по событию документа «DOMContentLoaded»), поэтому некоторые веб-ресурсы, на которые ссылается страница, могут быть ещё не подгружены.
Работу браузерного JS-кода см. в файле project3.html (для простоты можно искать все вызовы console.log(...), файл в кодировке UTF-8).
procedure TForm1.Chromium1ConsoleMessage(Sender: TObject;
const Browser: ICefBrowser; const message, Source: ustring; line: Integer;
out Result: Boolean);
var Str: String;
begin
Str:=UTF8Encode(message);
if (UTF8Pos('!cef3_close',Str)=1) then Close; // Нажали кнопку «Выход»
if (UTF8Pos('!cef3_focus',Str)=1) then Form1.Chromium1.SetFocus;
Browser.MainFrame.ExecuteJavaScript( Utf8Decode('window.lz_cef3=1;'), 'about:blank', 0);
end;
Запретим смену URL-адреса (переход по ссылкам не будет работать), а так-же создание новых объектов браузера (отдельных окон).
Создадим константу MainUrl с постоянным адресом веб-страницы.
Примечание: все ресурсы с относительным адресом, на которые ссылается любая веб-страница (изображения, стили и т.д.), должны быть доступны относительно адреса самой веб-страницы, вне зависимости от местонахождения браузера или нашего десктопного приложения.
procedure TForm1.Chromium1BeforeBrowse(Sender: TObject;
const browser: ICefBrowser; const frame: ICefFrame;
const request: ICefRequest; isRedirect: Boolean; out Result: Boolean);
begin
if (UTF8Encode(Frame.Url)<>MainUrl) then
Result:=False else Result:=True; // Запрет смены адреса MainUrl
end;
procedure TForm1.Chromium1BeforePopup(Sender: TObject;
const browser: ICefBrowser; const frame: ICefFrame; const targetUrl, targetFrameName: ustring;
targetDisposition: TCefWindowOpenDisposition; userGesture: Boolean;
var popupFeatures: TCefPopupFeatures; var windowInfo: TCefWindowInfo; var client: ICefClient;
var settings: TCefBrowserSettings; var noJavascriptAccess: Boolean;
out Result: Boolean);
begin
Result:=True; // Запрет новых объектов браузера (окон)
end;
И наконец, отключим дефолтное контекстное меню «model.Clear» в обработчике события «OnBeforeContextMenu»
Несложно сделать собственное контекстное меню, хоть через JavaScript, хоть через Obiect Pascal, хоть совместно.
procedure TForm1.Chromium1BeforeContextMenu(Sender: TObject;
const Browser: ICefBrowser; const Frame: ICefFrame;
const params: ICefContextMenuParams; const model: ICefMenuModel);
begin
model.Clear; // Очистка контекстного меню
end;
Скачать project3.html в zip-архиве (33 Kb) - для выполнения совместно с рассмотренным здесь project3 для Lazarus.
project3.html нужно положить в папку, путь к которой прописан в константе const MainUrl
project3.lpr
program project3;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Interfaces, // this includes the LCL widgetset
Forms, Unit1
{ you can add units after this };
{$R *.res}
begin
RequireDerivedFormResource := True;
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Form1.Chromium1.Load(MainUrl);
Application.Run;
end.
unit1.pas
unit Unit1;
interface
{$mode objfpc}{$H+}
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
LazUTF8, cef3lcl, cef3types, cef3intf, cef3gui;
type
{ TForm1 }
TForm1 = class(TForm)
Chromium1: TChromium;
procedure Chromium1BeforeBrowse(Sender: TObject;
const browser: ICefBrowser; const frame: ICefFrame;
const request: ICefRequest; isRedirect: Boolean; out Result: Boolean);
procedure Chromium1BeforeContextMenu(Sender: TObject;
const Browser: ICefBrowser; const Frame: ICefFrame;
const params: ICefContextMenuParams; const model: ICefMenuModel);
procedure Chromium1BeforePopup(Sender: TObject; const browser: ICefBrowser;
const frame: ICefFrame; const targetUrl, targetFrameName: ustring;
targetDisposition: TCefWindowOpenDisposition; userGesture: Boolean;
var popupFeatures: TCefPopupFeatures; var windowInfo: TCefWindowInfo;
var client: ICefClient; var settings: TCefBrowserSettings;
var noJavascriptAccess: Boolean; out Result: Boolean);
procedure Chromium1ConsoleMessage(Sender: TObject;
const Browser: ICefBrowser; const message, Source: ustring;
line: Integer; out Result: Boolean);
procedure Chromium1LoadError(Sender: TObject;
const Browser: ICefBrowser; const Frame: ICefFrame;
errorCode: TCefErrorCode; const errorText, failedUrl: ustring);
private
{ private declarations }
public
{ public declarations }
end;
const
MainUrl = 'file://localhost/G:/Lazarus/.../project3.html';
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.Chromium1BeforeBrowse(Sender: TObject;
const browser: ICefBrowser; const frame: ICefFrame;
const request: ICefRequest; isRedirect: Boolean; out Result: Boolean);
begin
if (UTF8Encode(Frame.Url)<>MainUrl) then
Result:=False else Result:=True; // Запрет смены адреса MainUrl
end;
procedure TForm1.Chromium1BeforeContextMenu(Sender: TObject;
const Browser: ICefBrowser; const Frame: ICefFrame;
const params: ICefContextMenuParams; const model: ICefMenuModel);
begin
model.Clear; // Очистка контекстного меню
end;
procedure TForm1.Chromium1BeforePopup(Sender: TObject;
const browser: ICefBrowser; const frame: ICefFrame; const targetUrl, targetFrameName: ustring;
targetDisposition: TCefWindowOpenDisposition; userGesture: Boolean;
var popupFeatures: TCefPopupFeatures; var windowInfo: TCefWindowInfo; var client: ICefClient;
var settings: TCefBrowserSettings; var noJavascriptAccess: Boolean;
out Result: Boolean);
begin
Result:=True; // Запрет новых объектов браузера (окон)
end;
procedure TForm1.Chromium1ConsoleMessage(Sender: TObject;
const Browser: ICefBrowser; const message, Source: ustring; line: Integer;
out Result: Boolean);
var Str: String;
begin
Str:=UTF8Encode(message);
if (UTF8Pos('!cef3_close',Str)=1) then Close; // Нажали кнопку «Выход»
if (UTF8Pos('!cef3_focus',Str)=1) then Form1.Chromium1.SetFocus;
Browser.MainFrame.ExecuteJavaScript( Utf8Decode('window.lz_cef3=1;'), 'about:blank', 0);
end;
procedure TForm1.Chromium1LoadError(Sender: TObject;
const Browser: ICefBrowser; const Frame: ICefFrame;
errorCode: TCefErrorCode; const errorText, failedUrl: ustring);
var v: variant;
begin
v:=errorCode; // Ошибка загружки веб-страницы
ShowMessage('Ошибка загрузки страницы' + #13 + 'errorCode = ' + IntToStr(v) + ' ' +
UTF8Encode(errorText + #13 + failedUrl) + #13 + 'Попробуйте ещё раз');
end;
end.



