zoukankan      html  css  js  c++  java
  • 002.Delphi插件之QPlugins,菜单插件

    运行之后的效果如下,

    图一

    图二

    主界面代码如下

    unit Frm_Main;
    
    
    interface
    
    uses
      Winapi.Windows,
      Winapi.Messages,
      System.SysUtils,
      System.Variants,
      System.Classes,
      Vcl.Graphics,
      CommCtrl,
      Vcl.Controls,
      Vcl.Forms,
      Vcl.Dialogs,
      Vcl.Menus,
      QPlugins,
      qplugins_params,
      qplugins_base,
      Serv_Menu,
      System.ImageList,
      Vcl.ImgList,
      Vcl.ExtCtrls,
      Vcl.StdCtrls;
    
    type
      // 定义一个菜单服务
      TQMenuService = class(TQService, IQMenuService)
      protected
        // 注册, 在接口IQMenuService中定义,子类来实现它
        function RegisterMenu(const APath: PWideChar; AOnEvent: IQNotify; ADelimitor: WideChar)
          : IQMenuItem;
        // 注销,在接口IQMenuService中定义,子类来实现它
        procedure UnregisterMenu(const APath: PWideChar; AOnEvent: IQNotify; ADelimitor: WideChar);
      public
        constructor Create; overload;
        destructor Destroy; override;
      end;
    
      // 菜单服务的部分接口
      TQMenuItem = class(TInterfacedObject, IQMenuItem)
      private
      protected
        FMenuItem: TMenuItem;
        FOnClick:  IQNotify;
        FName:     string;
        // 菜单接口
        FParams: IQParams;
        function GetCaption: PWideChar;
        procedure SetCaption(const S: PWideChar);
        function GetHint: PWideChar;
        procedure SetHint(const S: PWideChar);
        function SetImage(AHandle: HBITMAP): Boolean;
        function getParams: IQParams;
        procedure setParams(AParams: IQParams);
        function GetParentMenu: IQMenuItem;
        procedure DoClick(ASender: TObject);
      public
        constructor Create(AMenuItem: TMenuItem; AOnClick: IQNotify); overload;
        property Name: string read FName write FName;
        property Params: IQParams read getParams write setParams;
      end;
    
      TForm_Main = class(TForm)
        MainMenu1: TMainMenu;
        ilMenus: TImageList;
        Label1: TLabel;
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
      private
        {Private declarations}
      public
        {Public declarations}
      end;
    
    var
      Form_Main: TForm_Main;
    
    implementation
    
    uses
      qstring;
    
    var
      MainMenu: TMainMenu; // 全局菜单变量
    {$R *.dfm}
      {TMenuService}
    
      // 创建菜单服务
    constructor TQMenuService.Create;
    begin
      inherited Create(IQMenuService, 'QMenuService');
    end;
    
    // 释放
    destructor TQMenuService.Destroy;
    begin
      inherited;
    end;
    
    // 注册菜单
    function TQMenuService.RegisterMenu(const APath: PWideChar; AOnEvent: IQNotify;
      ADelimitor: WideChar): IQMenuItem;
    var
      p:               PWideChar;
      AName:           QStringW;
      AMenu, ANewMenu: TMenuItem;
      AItem:           IQMenuItem;
      AChildMenu:      TQMenuItem;
      AIdx:            Integer;
      // 识别是哪个菜单
      function IndexOfMenuName: Integer;
      var
        I:     Integer;
        AIntf: IQMenuItem;
      begin
        Result := -1;
        //
        for I := 0 to AMenu.Count - 1 do
        begin
          // 将菜单保存的Tag转成菜单条目
          AIntf := IQMenuItem(Pointer(AMenu.Items[I].Tag));
          if (InstanceOf(AIntf) as TQMenuItem).Name = AName then
          begin
            Result := I;
            Break;
          end;
        end;
      end;
    
    begin
      // 菜单
      AMenu := Form_Main.MainMenu1.Items;
      // 如果菜单文本存在
      p := PWideChar(APath); // '/文件/ShowForm','/文件/Exit'
      while p^ <> #0 do
      begin
        // 字符串分解
        AName := DecodeTokenW(p, [ADelimitor], #0, True); // 文件,ShowForm
        // 存在
        if Length(AName) > 0 then
        begin
          // 菜单名
          AIdx := IndexOfMenuName;
          if AIdx = -1 then
          begin
            // 创建菜单
            ANewMenu := TMenuItem.Create(MainMenu);
            if p^ = #0 then
              AChildMenu := TQMenuItem.Create(ANewMenu, AOnEvent)
            else
            begin
              AChildMenu := TQMenuItem.Create(ANewMenu, nil);
            end;
            AChildMenu.Name := AName;
            // 返回添加的子菜单
            Result := AChildMenu;
            Result._AddRef;
            // 创建并添加子条目
            ANewMenu.Tag := IntPtr(Pointer(Result));
            ANewMenu.Caption := AName;
            AMenu.Add(ANewMenu);
            AMenu := ANewMenu;
          end
          else
          begin
            // 返回
            Result := IQMenuItem(Pointer(AMenu.Items[AIdx].Tag));
            AMenu := AMenu.Items[AIdx];
          end;
        end;
      end;
    end;
    
    procedure TQMenuService.UnregisterMenu(const APath: PWideChar; AOnEvent: IQNotify;
      ADelimitor: WideChar);
    begin
    end;
    
    // 创建
    procedure TForm_Main.FormCreate(Sender: TObject);
    begin
      // 定义的菜单赋值
      MainMenu := MainMenu1;
      // 注册一个菜单服务,TQMenuService有多个功能,但IQMenuService只有注册和注销
      RegisterServices('/Services/Menus', [TQMenuService.Create(IQMenuService, 'MenuService')]);
      // 通过服务接口ID获取服务接口实例
      PluginsManager.ById(IQMenuService);
    end;
    {TQMenuItem}
    
    // 创建菜单
    constructor TQMenuItem.Create(AMenuItem: TMenuItem; AOnClick: IQNotify);
    var
      ATemp: Pointer;
    begin
      inherited Create;
      // 关联菜单的单击事件
      FMenuItem := AMenuItem;
      // 关联菜单单击事件
      FMenuItem.OnClick := DoClick;
      FOnClick := AOnClick;
    end;
    
    // 菜单点击事件
    procedure TQMenuItem.DoClick(ASender: TObject);
    var
      AFireNext: Boolean;
    begin
      AFireNext := True;
      //
      if Assigned(FOnClick) then
      begin
        FOnClick.Notify(MN_CLICK, Params, AFireNext); // 演示版本,不传递参数
      end;
    end;
    
    // 菜单接口_读取标题
    function TQMenuItem.GetCaption: PWideChar;
    begin
      Result := PWideChar(FMenuItem.Caption);
    end;
    
    // 菜单接口_读取Hint
    function TQMenuItem.GetHint: PWideChar;
    begin
      Result := PWideChar(FMenuItem.Hint);
    end;
    
    // 菜单接口_读取参数
    function TQMenuItem.getParams: IQParams;
    begin
      Result := FParams;
    end;
    
    // 菜单接口_读取父菜单
    function TQMenuItem.GetParentMenu: IQMenuItem;
    begin
      if Assigned(FMenuItem.Parent) then
        Result := IQMenuItem(FMenuItem.Parent.Tag)
      else
        Result := nil;
    end;
    
    // 菜单接口_设置标题
    procedure TQMenuItem.SetCaption(const S: PWideChar);
    begin
      FMenuItem.Caption := S;
    end;
    
    // 菜单接口_设置Hint
    procedure TQMenuItem.SetHint(const S: PWideChar);
    begin
      FMenuItem.Hint := S;
    end;
    
    // 菜单接口_设置图片
    function TQMenuItem.SetImage(AHandle: HBITMAP): Boolean;
    var
      ABitmap: TBitmap;
      AIcon:   TBitmap;
      AImages: TCustomImageList;
    begin
      AImages := (FMenuItem.Owner as TMenu).Images;
      AIcon := nil;
      ABitmap := TBitmap.Create;
      try
        ABitmap.Handle := AHandle;
        // 图标尺寸如果不对,则生成临时的位图,否则ImageList会添加失败
        if (ABitmap.Width <> AImages.Width) or (ABitmap.Height <> AImages.Height) then
        begin
          // 图标
          AIcon := TBitmap.Create;
          AIcon.SetSize(AImages.Width, AImages.Height);
          AIcon.Canvas.Brush.Color := ABitmap.TransparentColor;
          AIcon.Canvas.FillRect(Rect(0, 0, AImages.Width, AImages.Height));
          AIcon.Canvas.Draw((AImages.Width - ABitmap.Width) shr 1, (AImages.Height - ABitmap.Height)
            shr 1, ABitmap);
          AIcon.Transparent := True;
          FMenuItem.ImageIndex := AImages.AddMasked(AIcon, ABitmap.TransparentColor);
        end
        else
          FMenuItem.ImageIndex := AImages.AddMasked(ABitmap, ABitmap.TransparentColor);
      finally
        FreeAndNil(AIcon);
        FreeAndNil(ABitmap);
      end;
      Result := FMenuItem.ImageIndex <> -1;
    end;
    
    // 菜单接口_设置参数
    procedure TQMenuItem.setParams(AParams: IQParams);
    begin
      FParams := AParams;
    end;
    
    // 关闭窗口
    procedure TForm_Main.FormDestroy(Sender: TObject);
    // 销毁菜单
      procedure DestoryMenus(AParent: TMenuItem);
      var
        I:     Integer;
        AMenu: TMenuItem;
      begin
        for I := 0 to AParent.Count - 1 do
        begin
          AMenu := AParent.Items[I];
          // 只要菜单Tag存在,这释放销毁
          if AMenu.Tag <> 0 then
          begin
            IQMenuItem(Pointer(AMenu.Tag))._Release;
          end;
          DestoryMenus(AMenu);
        end;
      end;
    
    begin
      // 销毁菜单
      DestoryMenus(MainMenu.Items);
    end;
    
    end.

    Serv_Menu代码如下

    unit Serv_Menu;
    
    
    interface
    
    uses
      windows,
      menus,
      QPlugins,
      qplugins_base,
      qplugins_params;
    
    const
      // 传递的参数
      MN_CLICK = 0;
    
    type
      // 这里只实现了菜单服务的部分接口,如果要实现更多的接口,请自己扩展实现
      IQMenuItem = interface
        ['{83323919-93DE-4D40-87FB-7266AE804D6C}']
        function GetCaption: PWideChar;
        procedure SetCaption(const S: PWideChar);
        function GetHint: PWideChar;
        procedure SetHint(const S: PWideChar);
        function GetParams: IQParams;
        procedure SetParams(AParams: IQParams);
        function SetImage(AHandle: HBITMAP): Boolean;
        function GetParentMenu: IQMenuItem;
        // 菜单的四个属性,标题/Hint/父菜单/参数
        property Caption: PWideChar read GetCaption write SetCaption;
        property Hint: PWideChar read GetHint write SetHint;
        property ParentMenu: IQMenuItem read GetParentMenu;
        property Params: IQParams read GetParams write SetParams;
      end;
    
      IQMenuService = interface
        ['{667BD198-2F9A-445C-8A7D-B85C4B222DFC}']
        // 注册, 在接口中定义,子类来实现它
        function RegisterMenu(const APath: PWideChar; AOnEvent: IQNotify; ADelimitor: WideChar = '/'): IQMenuItem;
        // 注销, 在接口中定义,子类来实现它
        procedure UnregisterMenu(const APath: PWideChar; AOnEvent: IQNotify; ADelimitor: WideChar = '/');
      end;
    
    implementation
    
    end.

    菜单窗口代码如下

    unit Frm_Menu;
    
    
    interface
    
    uses
      Winapi.Windows,
      Winapi.Messages,
      System.SysUtils,
      System.Variants,
      System.Classes,
      Vcl.Graphics,
      Vcl.Controls,
      Vcl.Forms,
      Vcl.Dialogs,
      qplugins,
      qplugins_params,
      qplugins_base,
      Serv_Menu,
      Vcl.StdCtrls,
      Vcl.ExtCtrls;
    
    type
      TForm_Menu = class(TForm)
        Label1: TLabel;
        Image1: TImage;
        Memo1: TMemo;
      private
        {Private declarations}
      public
        {Public declarations}
      end;
    
    var
      Form_Menu: TForm_Menu;
    
    implementation
    
    {$R *.dfm}
    
    
    type
      // 通知响应接口
      TShowFormAction = class(TQInterfacedObject, IQNotify)
      protected
        procedure Notify(const AId: Cardinal; AParams: IQParams; var AFireNext: Boolean); stdcall;
      end;
      {TShowFormAction}
    
      // 通知响应接口
    procedure TShowFormAction.Notify(const AId: Cardinal; AParams: IQParams; var AFireNext: Boolean);
    var
      F:     TForm_Menu;
      I:     Integer;
      AName: string;
    begin
      // 如果有传过来参数,参数名字为'Exit'者退出程序
      if Assigned(AParams) and (ParamAsString(AParams.ByName('Name')) = 'Exit') then
        Application.Terminate
      else
      begin
        // 创建窗口
        F := TForm_Menu.Create(Application);
        // Memo输出
        with F.Memo1.Lines do
        begin
          BeginUpdate;
          try
            // 输出传过来的参数
            for I := 0 to AParams.Count - 1 do
            begin
              // 窗口输出参数
              Add(IntToStr(I) + ': ' + AParams[I].Name + '=' + ParamAsString(AParams[I]));
            end;
          finally
            EndUpdate;
          end;
        end;
        // 显示窗口
        F.ShowModal;
        F.Free;
      end;
    end;
    
    var
      // 通知响应接口,关注某个通知时,应实现IQNotify接口,以便接收相关的通知
      AShowForm: IQNotify;
    
      // 添加菜单相关内容
    procedure DoMenuServiceReady(const AService: IQService); stdcall;
    var
      F: TForm_Menu;
    begin
      // 菜单回调函数
      with AService as IQMenuService do
      begin
        // 通知响应接口
        AShowForm := TShowFormAction.Create;
        // 注册菜单
        with RegisterMenu('/文件/ShowForm', AShowForm) do
        begin
          // 窗体信息
          Caption := '显示窗体(&S)';
          F := TForm_Menu.Create(nil);
          // 设置图标
          SetImage(TBitmap(F.Image1.Picture.Graphic).Handle);
          Params := NewParams([1, 'Hello,world']);
          F.Free;
        end;
        // 注册第二个菜单
        with RegisterMenu('/文件/Exit', AShowForm) do
        begin
          Caption := '退出(&X)';
          Params := NewParams([]);
          // 参数名字为Exit
          Params.Add('Name', ptUnicodeString).AsString := NewString('Exit');
        end;
      end;
    end;
    
    // initialization在单元中放在文件结尾前,包含用来初始化单元的代码,它在主程序运行前运行并且只运行一次。
    initialization
    
    // 通知响应接口
    AShowForm := nil;
    // 等待指定的服务注册,DoMenuServiceReady为服务注册完成时的通知回调
    PluginsManager.WaitService(IQMenuService, DoMenuServiceReady);
    
    // 在单元中放在 initialization 和 end. 之间,包含了单元退出时的代码。在程序退出时运行并且只运行一次。
    finalization
    
    // 检查菜单接口是否存在,存在则释放菜单功能
    if Assigned(AShowForm) then
    begin
      // 释放菜单功能
      with PluginsManager as IQMenuService do
      begin
        UnregisterMenu('/File/ShowForm', AShowForm);
      end;
      AShowForm := nil;
    end;
    
    end.
  • 相关阅读:
    LINUX下使用crontab进行RMAN备份实验
    cocos2d-x 通过JNI实现c/c++和Android的java层函数互调
    整型与字符型之间转化
    MFC的最大化,最小化,关闭
    [置顶] IT屌丝的离职申请
    The Priest Mathematician
    jQuery入门学习贴
    poj3308Paratroopers(最小割)
    Nginx 开启 debug 日志的办法
    关于产品的一些思考——(四十二)网易之有道云笔记协同版
  • 原文地址:https://www.cnblogs.com/tianpan2019/p/11489162.html
Copyright © 2011-2022 走看看