How to create MDI Form using BPL and FireDAC of one connection with Rad Studio Delphi 11.x


Part1 
BPL MDI Form Project


Part2

FireDAC single connection on MDI Form


 Project Sample Source Download

MDI Form 프로젝트를 BPL 패키지 방식으로 만들면 기존 DLL 방식 보다 간편하고 안정적인 MDI 프로젝트 생성이 가능해 집니다.

각 Child 폼들은 각각의 패키지 프로젝트로 구성 되어 빌드시 .bpl 타입의 독립 모듈이 생성 됩니다.

따라서 각각의 Form들을 별도의 프로젝트로 구축 할 수 있으므로 Form이 많은  대형 프로젝트에서 분업화 된 작업이 가능하며 메인 Form의 .EXE 사이즈도 적은 용량으로 유지 보수가 좀 더 편리 합니다.

물론 BPL은 델파이 내부에서만 사용되며 그래서 더욱 연동에 유리 합니다.

또한 각각의 Child Form들은 Main 폼에서 생성한 FireDAC 커넥션을 사용 할 수 있어 단일 커넥션으로 데이터베이스 접속이 가능 합니다.

데모 영상은 1부와 2부로 나뉘어져 있고 영상을 먼저 시청하고 아래 요약된 내용을 보시면 쉽게 이해 할 수 있습니다.

1부는 BPL MDI 프로젝트를 구축 하는 방법이고

2부는 작성된 MDI Form 에서 단일 커넥션으로 각각의 Child Form들이 데이터베이스에 접속 하는 방법을 소개 합니다.

샘플프로젝트는 즉시 실행 해 볼 수 있도록 편의상 SQLite를 사용 하였으므로 실무에서는 사용 가능한 RDB를 이용하면 됩니다.


Part 1. BPL 로 MDI Project 만들기

1. 프로젝트 폴더 분류

- Output 폴더 생성 : Exe 및 BPL 위치로 사용


2. Main Form 프로젝트 생성

 - FormStyle : MDIForm 설정

 - 프로젝트 옵션 > Output Directory 지정


2. Child Form 프로젝트 생성

- 프로젝트 그룹에서 Add New Project > Pacjage 선택

- bpl 프로젝트 Contains 우클릭 > Add New > VCL Form 으로 새폼 추가

- FormStyle : MDI Child 설정

- 프로젝트 옵션 > Package Output 폴더 지정

- 패키지 프로젝드 빌드 : 패키지 등록창에서 OK 선택.


3. 같은 방법으로 Child Form 프로젝트 1개 더 생성

4. Main Form 프로젝트

- Project Option > Packages > RunTime Packages > Link with RunTime Packages : TRUE 설정

- 프로젝트 정상 빌드 확인.


5. Main Form 프로젝트 화면 및 소스 작업 (상세내용 샘플 프로젝트 참조)

- TMainMenu 로 Child 창을 열기 위한 풀다운 메뉴 생성

- 각 메뉴 클릭시 Child Form 호출


6. Child Form 프로젝트 화면 및 소스 작업 (상세내용 샘플 프로젝트 참조)

- Initialization  와 finalization  추가

- Form OnClose 이벤트에 Form 메모리 제거


7. 실행

- Child Form 프로젝트 및 Main Form 프로젝트 각각 빌드

- 실행은 Main Form 프로젝트 (.Exe)

- Child Form 프로젝트만 변경시 해당 bpl 프로젝트만 개별 빌드 하면 됨, 단 Main Form Exe는 재실행 


Part 2. 각 Child Form에서 단일 Connection 으로 FIreDAC 사용하기


1. Main Form 프로젝트

- TFDConnection 으로 Database Connetion 설정


2. Child Form 프로젝트

- Main Form 프로젝트에서 설정한 TFDConnection 커넥션 지정

Ex) TFDQuery 의 Connection을 "MForm.FDConnection1: 으로 지정 (상세내용 샘플 프로젝트 참조)

Nave Papago Open API Project with Delphi FMX

 네이버에서 제공하는 다양한 OPEN API 중 활용도가 놓은 Papago 번역 서비스의 활용법을 소개 합니다.

사용환경은 Rad Studio 11 이며 Post 호출 방식입니다.

REST API를 이용한 사용법을 알면 다른 여러가지 OPEN API도 같은 방식으로 사용이 가능 합니다.

1.  https://developers.naver.com/main/  네이버 개발자 사이트에서 파파고 서비스를 선택하여 어플리케이션을 등록하고 Client ID 와 Client Secret 키를 발급 받습니다. 테스트를 위해서는 어플리케이션 이름과 Android Package (또는 iOS) 명을 임의로 지정해도 됩니다. 향후 정식 서비스에서는 실제 사용하는 패키지명을 등록 하면 됩니다.

https://developers.naver.com/products/papago/nmt/nmt.md 


image.png

image.png


2. Rad Studio 에서 새로운  FMX 프로젝트를 생성하고 아래 그림과 같이 화면을 생성하고  TNetHTTPRequest  TNetHTTPClient 컴포넌트를 생성 합니다. 이때 TNetHTTPRequest 에 연결된 Client 항목을 TNetHTTPClient 로 지정 합니다.

image.png


3. 아래 소스를 참고 하여 빌드 하면 파파고 서비스를 호출하여 사용 가능 합니다. 

번역 버튼을 클릭하면 파파고 서비스를 호출하게되고 NetHTTPRequest1의  OnRequestCompleted 메소드에서 결과값을 받아 옵니다.

NetHTTPClient1.CustomHeaders 에 본인이 발급받은 Cient ID 와 키값을 입력하고

sParam.Add( 'text=' + Edit1.Text ); 의 Edit1.Text 번역하고자 하는 내용을 입력합니다.

기타 옵션은 네이버 개발자 가이드를 참고 하시면 됩니다.


procedure TForm1.Button1Click(Sender: TObject);
var
  sParam : TStringList;
  sURL : string;
begin
  sURL := 'https://openapi.naver.com/v1/papago/n2mt';


  NetHTTPClient1.Accept        :=  'application/json';
  NetHTTPClient1.ContentType   :=  'application/x-www-form-urlencoded; charset=UTF-8;';
  NetHTTPClient1.AcceptCharSet :=  'UTF-8';

  NetHTTPClient1.CustomHeaders['X-Naver-Client-Id']     := 'abcd';     // 애플리케이션 등록 시 발급받은 클라이언트 아이디 값
  NetHTTPClient1.CustomHeaders['X-Naver-Client-Secret'] := '1234';  // 애플리케이션 등록 시 발급받은 클라이언트 시크릿 값


  sParam := TStringList.Create;
  sParam.Add( 'source=ko' );
  sParam.Add( 'target=en' );
  sParam.Add( 'text=' + Edit1.Text );


  NetHTTPRequest1.Post(  sURL, sParam ) ;

end;


procedure TForm1.NetHTTPRequest1RequestCompleted(const Sender: TObject; const AResponse: IHTTPResponse);
begin
  Memo2.Lines.Clear;
  Memo2.Lines.Add(  AResponse.ContentAsString() );   // 응답받은 전체 Json 데이터

  Memo1.Lines.Clear;
  Memo1.Lines.Add(  Response_To_GetPapagoData( AResponse.ContentAsString() ) );  // 번역된 내용 
end;


//--------------------------------------------------------------------------------
function TForm1.Response_To_GetPapagoData( rData : string ) : string;
var
  jSONValue : TJSONValue;
begin

// https://developers.naver.com/docs/papago/papago-nmt-api-reference.md

// 결과 Json 데이터 샘플
// {
//    "message": {
//        "@type": "response",
//        "@service": "naverservice.nmt.proxy",
//        "@version": "1.0.0",
//        "result": {
//            "srcLangType":"ko",
//            "tarLangType":"en",
//            "translatedText": "tea"      <<<<< 번역된 결과 값 항목.
//        }
//    }
// }


   JsonValue := TJSonObject.ParseJSONValue( rData );
   result := JsonValue.GetValue<string>('message.result.translatedText');

   JsonValue.Free;
end;


* 결과 화면 

image.png




Vibration Metronome App Source 





Rapid Design FMX Component



Delphi Source Code

unit MVUnit;


interface


uses

  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,

  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Layouts,

  FMX.Objects, FMX.Controls.Presentation, FMX.StdCtrls,


  RDuDigitalNumber, RDuHorzSlideBar, RDuRotaryKnob, RDuAngularGauge, FMX.Media, RDuColorCheckBox, RDuButtonSet;


type

  TForm1 = class(TForm)

    RDAngularGauge1: TRDAngularGauge;

    RDRotaryKnob1: TRDRotaryKnob;

    RDHorzSlideBar1: TRDHorzSlideBar;

    RDN2: TRDDigitalNumber;

    RDN3: TRDDigitalNumber;

    RDN1: TRDDigitalNumber;

    Timer1: TTimer;

    Layout1: TLayout;

    Text1: TText;

    Layout2: TLayout;

    Layout3: TLayout;

    RDButtonSet1: TRDButtonSet;

    RDColorCheckBox1: TRDColorCheckBox;

    procedure RDRotaryKnob1DialChange(Sender: TObject);

    procedure RDHorzSlideBar1Change(Sender: TObject);

    procedure Timer1Timer(Sender: TObject);

    procedure FormKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);

    procedure RDButtonSet1ButtonSetClick(Sender: TObject);

    procedure FormCreate(Sender: TObject);

  private

    procedure One_Vibration;

    { Private declarations }

  public

    { Public declarations }

  end;


var

  Form1: TForm1;


  BPM : integer = 60;

  Count : integer = 0;

  IsInc : boolean = TRUE;



implementation


{$IFDEF ANDROID}

uses

   Androidapi.JNI.Os,

   Androidapi.JNI.GraphicsContentViewText,

   Androidapi.Helpers,

   Androidapi.JNIBridge;

{$ENDIF}



{$R *.fmx}



procedure TForm1.FormCreate(Sender: TObject);

begin

  RDAngularGauge1.Value :=0;

end;



procedure TForm1.Timer1Timer(Sender: TObject);

begin

  One_Vibration();


  if Count = 4 then

     IsInc := FALSE

  else if Count = 0 then

     IsInc := TRUE;


  if IsInc then Inc( Count )

  else          Dec( Count );


  RDAngularGauge1.Value := Count mod 5;

end;



//--------------------------------------------------

procedure TForm1.One_Vibration();

{$IFDEF ANDROID}

Var

  Vibrator:JVibrator;

{$ENDIF}


begin

{$IFDEF ANDROID}

  Vibrator:=TJVibrator.Wrap((SharedActivityContext.getSystemService(TJContext.JavaClass.VIBRATOR_SERVICE) as ILocalObject).GetObjectID);

  Vibrator.vibrate(100);   // milliseconds

{$ENDIF}


{$IFDEF MSwindows}

  beep;

{$ENDIF}


end;



procedure TForm1.RDRotaryKnob1DialChange(Sender: TObject);

var

  b1 : integer;

begin

  b1 := Round( RDRotaryKnob1.Value ) div 10 + 3 ;


  BPM := b1 * 10;


  if b1 < 10 then

  begin

    RDN1.Number := '0';

    RDN2.Number := b1.ToString

  end

  else

  begin

    RDN1.Number := '1';

    RDN2.Number := (b1 -10).ToString;

  end;


  BPM := RDN1.Number.ToInteger*100 + RDN2.Number.ToInteger*10 + RDN3.Number.ToInteger;

//  BPMText.Text := BPM.ToString;

end;


procedure TForm1.RDHorzSlideBar1Change(Sender: TObject);

var

  b2 : integer;

begin

  b2 := Round( RDHorzSlideBar1.Value );

  RDN3.Number := b2.ToString;


  BPM := RDN1.Number.ToInteger*100 + RDN2.Number.ToInteger*10 + RDN3.Number.ToInteger;

//  BPMText.Text := BPM.ToString;

end;


procedure TForm1.RDButtonSet1ButtonSetClick(Sender: TObject);

begin

  if RDColorCheckBox1.IsChecked = TRUE then

  begin

     case RDButtonSet1.ClickIndex of

       0 : begin

             Timer1.Enabled := TRUE;

             Timer1.Interval := Round( 60 / BPM.ToSingle * 1000 );    //Timer1.Interval := 60 div BPM * 1000;

           end;


       1 : begin

             Count := 0;

             IsInc := TRUE;


             RDAngularGauge1.Value := 0.0;

             Timer1.Enabled := FALSE;

           end;


       2 : Close;

     end;

  end;

end;




procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState);

begin

{$IFDEF ANDROID}

   if Key = vkHardwareBack Then

      Key :=  0 ;                // 기본액션인 앱 종료를 방지함.

{$ENDIF}

end;




end.




Random number assignment without duplicates

Random number assignment without duplicates

type

  TDyArray = array of Integer;

//-----------------------------------------------------------

function Out_Random( cno : integer ) : TDyArray;

var

  r1, r2, itemp, i : integer;

  dataArr : TDyArray;

begin

  SetLength( dataArr, cno );

  for i := 0 to cno-1 do   // 동적 배열에 데이터를 할당

    dataArr[i] := i + 1;

  for I := 1 to cno div 2  do  // 전체갯수 반번 만큼 랜던 교환

  begin

    r1 := Random( cno );

    r2 := Random( cno );

    itemp := dataArr[ r1 ];

    dataArr[ r1 ] := dataArr[ r2 ];

    dataArr[ r2 ] := itemp;

  end;

  result := dataArr;

end;

// for Test ***************************************************************************

procedure TMForm.Button2Click(Sender: TObject);

var

  aaa : TDyArray;

  i  : integer;

begin

  aaa := Out_Random( 10 );


  Memo1.Lines.Clear;

  for i := 0 to High( aaa ) do

    Memo1.Lines.Add( aaa[ i ].ToString );

end;




How to view Android package apps and call SMS or phone apps in Delphi FMX

 


unit Gradation;

unit MUnit1;

interface

uses

  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,

  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls,

  Androidapi.JNI.GraphicsContentViewText,

  Androidapi.JNI.JavaTypes,

  FMX.Helpers.Android,

  AndroidApi.Helpers,

  Androidapi.JNIBridge,

  Androidapi.JNI.Net;


type

  TForm1 = class(TForm)

   Button1: TButton;

    Button2: TButton;

    procedure Button1Click(Sender: TObject);

    procedure Button2Click(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;

var

  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.Button1Click(Sender: TObject);

var

  PM: JPackageManager;

  mainIntent: JIntent;

  LaunchIntent: JIntent;

  pkgAppsList: JList;

  ri: JResolveInfo;

  iter: JIterator;

  midlist : TStringList;

begin

  PM := SharedActivityContext.getPackageManager;

  mainIntent := TJIntent.JavaClass.init(TJIntent.JavaClass.ACTION_MAIN, nil);

  mainIntent.addCategory(TJIntent.JavaClass.CATEGORY_LAUNCHER);

  pkgAppsList := PM.queryIntentActivities(mainIntent, 0);

  midlist := TStringList.Create;

  iter := pkgAppsList.iterator;

  while iter.hasNext do

  begin

    ri := TJResolveInfo.Wrap((iter.next as ILocalObject).GetObjectID);

    mIdList.Add(JStringToString(ri.activityInfo.applicationInfo.packageName));

  end;

  LaunchIntent := PM.getLaunchIntentForPackage(StringToJString(mIdList[2]));

// 특정 앱  실행 방법.

// LaunchIntent := PM.getLaunchIntentForPackage(StringToJString( 'com.samsung.android.messaging') );

// SharedActivityContext.startActivity( LaunchIntent )

  showmessage(  mIdList.Text );

end;



procedure TForm1.Button2Click(Sender: TObject);

var

  PM: JPackageManager;

  Intent: JIntent; //Declares the intent object

begin

  PM := SharedActivityContext.getPackageManager;

  Intent := TJIntent.Create;

  Intent.setData( TJnet_Uri.JavaClass.parse(StringToJString('smsto:01012345678')));   // sms

// Intent.setData(TJnet_Uri.JavaClass.parse(StringToJString('tel:' + ANumber)));      // call

  Intent.putExtra(TJIntent.JavaClass.EXTRA_TEXT, StringtoJString( 'Hellow' ));

  Intent.setAction(TJIntent.JavaClass.ACTION_VIEW); //Defines the Action.

  if SharedActivityContext.getPackageManager.queryIntentActivities(Intent, TJPackageManager.JavaClass.MATCH_DEFAULT_ONLY).size > 0 then //Checks if there is at least one application capable of receiving the intent.

     SharedActivityContext.startActivity(Intent) //Calls startActivity() to send the intent to the system.

  else

    ShowMessage('Receiver not found');

end;


end.

YouTube Mood Lights with gradient colors(Screensaver) FMX Project

 I open the source of the video production, which is recording very high views.

With Delphi FMX projects, you can easily create videos like this.

Let's try it.