Плагины как компилируемая часть

Общие вопросы программирования, алгоритмы и т.п.

Модератор: Модераторы

Плагины как компилируемая часть

Сообщение Sharfik » 13.09.2025 04:19:21

Для обсуждения. Обычно понятие "плагин" это что то подключаемое к программе в процессе ее работы. В случае с FPC и Lazarus это так же реализуемо, но есть один недостаток - форма из плагина не может быть внедрена в формы основного приложения. В то же время, одно какое то свое решение, наработка может использоваться в похожих проектах. Например, диалог редактирования параметров "контракта" может быть доступен как из приложения пользователя, так и из приложения администратора. Если вдруг их разделят... Или же основной приложение может разрабатывать один человек, а надстройки другой. И нужно просто по быстрому подцепить надстройку.
И вот тут в мою больную голову пришла мысль - "Хочу просто добавлять модуль формы в состав приложения, и чтобы все везде прописывалось само.". И ведь можно сделать.
Каждый модуль pascal имеет возможность описать секции инициализации и финализации. А значит, написав единый глобальный класс можно сослаться на него у модулей форм, команд, которые используются как плагины. Например разрабатываются не автором основной программы, а только компилируются как финальный результат.
Ну а в главном модулей приложения идет просто цикл типовой загрузки. Выглядит это все как ниже показано.

Глобальный модуль и класс доступный всем надстройкам.
Код: Выделить всё
unit u_modules;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils, Forms, contnrs;

type

{ Data types }

TModuleProcedureEvent = procedure (Sender: TObject);
TModuleCreateFormEvent = procedure (Sender: TObject; var ANewForm:TForm);
TModuleRunCommandEvent = procedure (ARunCmd:ShortString; AParamValue,AParamName:TStringArray; var AResult:integer);

{ Forward Declarartions }

TAppModuleList = class;
TAppModuleItem = class;

{ TAppModuleItem class }

TAppModuleItem = class
private
  FActive            : Boolean;
  FCollectionName    : ShortString;
  FID                : ShortString;
  FGroupID           : ShortString;
  FModuleType        : integer;
  FIconIndex         : Integer;
  FPositionIndex     : Integer;
  FOnInitModules     : TModuleProcedureEvent;
  FOnDeinitModules   : TModuleProcedureEvent;
  FOnCreateFormEvent : TModuleCreateFormEvent;
  FOnRunCommandEvent : TModuleRunCommandEvent;

published
  property Active        : Boolean read FActive write FActive;
  property ModuleType    : Integer read FModuleType write FModuleType;
  property ID            : ShortString read FID write FID;
  property GroupID       : ShortString read FGroupID write FGroupID;
  property IconIndex     : Integer read FIconIndex write FIconIndex;
  property PositionIndex : Integer read FPositionIndex write FPositionIndex;

public
  property OnInitModules     : TModuleProcedureEvent read FOnInitModules write FOnInitModules;
  property OnDeinitModules   : TModuleProcedureEvent read FOnDeinitModules write FOnDeinitModules;
  property OnCreateFormEvent : TModuleCreateFormEvent read FOnCreateFormEvent write FOnCreateFormEvent;
  property OnRunCommandEvent : TModuleRunCommandEvent read FOnRunCommandEvent write FOnRunCommandEvent;

  procedure InitModules(Sender: TObject);
  procedure DeinitModules(Sender: TObject);
  procedure CreateForm(Sender: TObject; var ANewForm:TForm);
  procedure RunCommand(ARunCmd:ShortString; AParamValue,AParamName:TStringArray; var AResult:integer);
  constructor Create;
  destructor Destroy; override;
end;

{ TAppModuleList class }

TAppModuleList = class(TObjectList)
private
  function GetItem(Index: Integer): TAppModuleItem;
  procedure SetItem(Index: Integer; const Value: TAppModuleItem);
public
  property Items[Index: Integer]: TAppModuleItem read GetItem
                                                       write SetItem; default;
  procedure Insert(Index: Integer; Item: TAppModuleItem);
  function Add(Item: TAppModuleItem): Integer;
  procedure Clear;
  //Создание объекта без вставки его в список
  function CreateItem: TAppModuleItem; virtual;
  constructor Create;
  destructor Destroy; override;
end;

const
  MODULETYPE_UNKNOW = 0;
  MODULETYPE_BASE   = 1000;
  MODULETYPE_CMD    = MODULETYPE_BASE+1;
  MODULETYPE_GUI    = MODULETYPE_BASE+2;

var
  GlobalModulesList : TAppModuleList;

implementation


{ TAppModuleList }

function TAppModuleList.GetItem(Index: Integer): TAppModuleItem;
begin
  Result := TAppModuleItem(inherited GetItem(Index));
end;

procedure TAppModuleList.SetItem(Index: Integer;
  const Value: TAppModuleItem);
begin
  inherited SetItem(Index, Value);
end;

procedure TAppModuleList.Insert(Index:Integer; Item:TAppModuleItem);
begin
  inherited Insert(Index, Item);
end;

function TAppModuleList.Add(Item: TAppModuleItem): Integer;
begin
  Result := inherited Add(Item);
end;

procedure TAppModuleList.Clear;
var
  i:    Integer;
  item: TAppModuleItem;
begin
  for i := Count - 1 downto 0 do
  begin
      item := Items[i];
      item.Free;
      Extract(item);
  end;
  inherited Clear;
end;

function TAppModuleList.CreateItem: TAppModuleItem;
begin
  Result := TAppModuleItem.Create;
end;

constructor TAppModuleList.Create;
begin
  inherited Create;
end;

destructor TAppModuleList.Destroy;
begin
  inherited Destroy;
end;

{ TAppModuleItem }

procedure TAppModuleItem.InitModules(Sender: TObject);
begin
  if Assigned(FOnInitModules) then
     FOnInitModules(Sender);
end;

procedure TAppModuleItem.DeinitModules(Sender: TObject);
begin
  if Assigned(FOnDeInitModules) then
     FOnDeInitModules(Sender);
end;

procedure TAppModuleItem.RunCommand(ARunCmd: ShortString; AParamValue,
  AParamName: TStringArray; var AResult: integer);
begin
  if Assigned(FOnRunCommandEvent) then
     FOnRunCommandEvent(ARunCmd,AParamValue,AParamName,AResult)
  else
     AResult:=0;
end;

procedure TAppModuleItem.CreateForm(Sender: TObject; var ANewForm: TForm);
begin
  ANewForm:=nil;
  if Assigned(FOnCreateFormEvent) then
     FOnCreateFormEvent(Sender, ANewForm);
end;

constructor TAppModuleItem.Create;
begin
  inherited Create;
  FActive            :=True;
  FModuleType        :=MODULETYPE_UNKNOW;
  FID                :='';
  FGroupID           :='';
  FIconIndex         :=-1;
  FPositionIndex     :=0;

  FOnInitModules     :=nil;
  FOnDeinitModules   :=nil;
  FOnCreateFormEvent :=nil;
  FOnRunCommandEvent :=nil;
end;

destructor TAppModuleItem.Destroy;
begin
  inherited Destroy;
end;

Initialization
  GlobalModulesList := TAppModuleList.Create;

finalization
  GlobalModulesList.Free;
end.

end.



Помимо него у всех форм должен быть какой то общий класс предок для обработки процедур.


В модуле главной формы делаем обработку глобального списка и создания всех кого нужно по нему.

Код: Выделить всё
...uses u_modules

procedure TfMain.FormCreate(Sender: TObject);
var
  sTemp,
  sTempFolder  :String;
  i            :integer;
  RootNode,
  DataRootNode,
  Node         :TTreeNode;
  FirstPageForm,
  NewForm      :TCustomModuleForm;
  NewTS        :TTabSheet;
  mdlItem      :TAppModuleItem;
begin

  ***

  FModules      :=TList.Create;

  ***

  pgcMain.Visible:=False;

  //FirstPage
  FirstPageForm                :=TFModuleFirstPage.Create(Self);
  NewForm                      :=FirstPageForm;
  NewForm.ModuleIconIndex      :=0;
  NewForm.GroupID              :=IDMENUGRP_GENERAL;
  NewForm.InitForm(TCPClient,FSettings.ActiveConnection);
  NewForm.OnDoSetStatus        :=@DoSetStatus;
  //NewForm.ManualDock(pgcMain);
  NewTS                        :=pgcMain.AddTabSheet;
  NewTS.Tag                    :=FModules.Add(NewForm);
  NewTS.Caption                :=NewForm.Name;
  NewForm.Tag                  :=NewTS.Tag;
  NewForm.ManualDock(NewTS);
  NewForm.Align:=alClient;
  NewForm.Show;
 
  ***

  //Users
  NewForm                      :=TFModuleUsersExt.Create(Self);
  NewForm.ModuleIconIndex      :=1;
  NewForm.GroupID              :=IDMENUGRP_ADMIN;
  NewForm.InitForm(TCPClient,FSettings.ActiveConnection);
  NewForm.OnDoSetStatus        :=@DoSetStatus;
  //NewForm.ManualDock(pgcMain);
  NewTS                        :=pgcMain.AddTabSheet;
  NewTS.Tag                    :=FModules.Add(NewForm);
  NewForm.Tag                  :=NewTS.Tag;
  NewTS.Caption                :=NewForm.Name;
  NewForm.ManualDock(NewTS);
  NewForm.Align                :=alClient;
  NewForm.Show;
 
  {
    Все формы в модулях создаются как наследники какого то базового общего класса
   TCustomModuleForm = class(TForm)
  }

  for i:=0 to GlobalModulesList.Count-1 do
  begin
      mdlItem:=GlobalModulesList.Items[i];
      if mdlItem.ModuleType<>MODULETYPE_GUI then
         Continue;
      mdlItem.CreateForm(Self,TForm(NewForm));
      //NewForm:=TCustomModuleForm(NewForm);
      if Assigned(NewForm) then
      begin
        NewForm.ModuleIconIndex      :=mdlItem.IconIndex;
        NewForm.GroupID              :=mdlItem.GroupID;

        NewForm.InitForm(TCPClient,FSettings.ActiveConnection);
        NewForm.OnDoSetStatus        :=@DoSetStatus;
        //NewForm.ManualDock(pgcMain);
        NewTS                        :=pgcMain.AddTabSheet;
        NewTS.Tag                    :=FModules.Add(NewForm);
        NewForm.Tag                  :=NewTS.Tag;
        NewTS.Caption                :=NewForm.Name;
        NewForm.ManualDock(NewTS);
        NewForm.Align                :=alClient;
        NewForm.Show;
      end;
  end;

  ***
 
end;


Ну и финалочка. Все разрабатываемые надстройки для автоматического добавления в программу должны в модуле своем иметь только небольшую вставку в конце
Если речь не о формах, а просто командах, то принцип такой же, но обрабатываются связки с прописываемыми процедурами. В u_modules так же предусмотрено.

Код: Выделить всё
uses u_modules
...

{Интеграция модуля в программу}

procedure CreateFormEvent(Sender: TObject; var ANewForm: TForm);
begin
  ANewForm:=TFModuleContracts.Create(TForm(Sender));
end;

procedure SetThisModuleToGlobalList;
const
  IDMENUGRP_GENERAL         ='IDMENUGRP_GENERAL';
var
   item:TAppModuleItem;
begin
   item                     :=GlobalModulesList.CreateItem;
   item.Active              :=False;
   item.ID                  :='{CONTRACTS}';
   item.GroupID             :=IDMENUGRP_GENERAL;
   item.IconIndex           :=5;
   item.PositionIndex       :=0;
   item.ModuleType          :=MODULETYPE_GUI;

   item.OnInitModules       :=nil;
   item.OnDeinitModules     :=nil;
   item.OnCreateFormEvent   :=@CreateFormEvent;

   GlobalModulesList.Add(item);
end;

Initialization
   SetThisModuleToGlobalList;

finalization

end.





Ваши мысли?
- Критика?
- Что не учел и чем это плохо?
- в каких модулях lazarus это уже реализовано до меня, и я изобрел велосипед не перекопав кучу треша из состава IDE?
Аватара пользователя
Sharfik
энтузиаст
 
Сообщения: 817
Зарегистрирован: 20.07.2013 01:04:30

Re: Плагины как компилируемая часть

Сообщение RRYTY » 13.09.2025 07:07:03

Sharfik писал(а):Ваши мысли?


Реализация объектно-ориентированного принципа программирования, четко по учебнику. Мои искренние поздравления.

Sharfik писал(а):Критика?


Отсутствует.

Sharfik писал(а):- Что не учел и чем это плохо?
- в каких модулях lazarus это уже реализовано до меня, и я изобрел велосипед не перекопав кучу треша из состава IDE?


Без комментариев.
RRYTY
постоялец
 
Сообщения: 247
Зарегистрирован: 25.12.2021 10:00:32

Re: Плагины как компилируемая часть

Сообщение Alex2013 » 13.09.2025 16:12:15

"- форма из плагина не может быть внедрена в формы основного приложения.".

Как это не может ? А процедура SetParent на что ?
Пример тут :
Браузер в качестве View в десктопных проектах (WebUI)
Зы
DeepSeek правда сказал что это "сомнительный метод " но чем он сомнительный если везде работает?
(От Win XP до Win 11) По идее аналог SetParent должен быть и в линукс . :idea:
https://www.purebasic.fr/english/viewtopic.php?t=49558
Alex2013
долгожитель
 
Сообщения: 3161
Зарегистрирован: 03.04.2013 11:59:44


Вернуться в Общее

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 7

Рейтинг@Mail.ru
cron