A colorful graphic show made with Delphi Firemonkey.
Using ChatGPT in Delphi
// ChatGPT Personal Key : https://beta.openai.com/account/api-keys
unit MainCGPT;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, System.JSON,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Memo.Types, FMX.StdCtrls, FMX.Controls.Presentation, FMX.ScrollBox,
FMX.Memo, System.Net.URLClient, System.Net.HttpClient, System.Net.HttpClientComponent, FMX.Layouts;
type
TMForm = class(TForm)
Memo_Ans: TMemo;
Memo_HanQ: TMemo;
BT_Question: TButton;
NetHTTPClient1: TNetHTTPClient;
NetHTTPRequest1: TNetHTTPRequest;
Label1: TLabel;
Label3: TLabel;
Layout1: TLayout;
Layout3: TLayout;
SpeedButton1: TSpeedButton;
procedure BT_QuestionClick(Sender: TObject);
procedure NetHTTPClient1RequestCompleted(const Sender: TObject; const AResponse: IHTTPResponse);
procedure Memo_HanQDblClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
MForm: TMForm;
// ChatGPT key : https://beta.openai.com/account/api-keys
Const MyGPTKey = 'mykey_1234abcd'; // Input your key
implementation
{$R *.fmx}
procedure TMForm.Memo_HanQDblClick(Sender: TObject);
begin
Memo_HanQ.Lines.Clear;
end;
// Question *********************************************
procedure TMForm.BT_QuestionClick(Sender: TObject);
var
LPostdata: string;
LPostDataStream: TStringStream;
begin
LPostData := '{' +
'"model": "text-davinci-003",'+
'"prompt": "' + Memo_HanQ.Text + '",'+
'"max_tokens": 2048,'+
'"temperature": 0'+
'}';
LPostDataStream := TStringStream.Create( LPostData, TEncoding.UTF8);
NetHTTPClient1.CustomHeaders['Authorization'] := 'Bearer ' + MyGPTKey;
NetHTTPClient1.CustomHeaders['Content-Type'] := 'application/json';
LPostDataStream.Position := 0;
NetHTTPClient1.Post('https://api.openai.com/v1/completions', LPostDataStream );
end;
// Answer ********************************************************************************
procedure TMForm.NetHTTPClient1RequestCompleted(const Sender: TObject; const AResponse: IHTTPResponse);
var
LString, ansStr : string;
LJson: TJsonObject;
begin
if AResponse.StatusCode = 200 then
begin
LString := AResponse.ContentAsString;
LJson := TJSONObject.ParseJSONValue(LString) as TJSONObject;
try
ansStr := LJson.GetValue('choices').A[0].FindValue('text').Value;
finally
LJson.Free;
end;
end
else
ansStr := 'HTTP response code: ' + AResponse.StatusCode.ToString;
Memo_Ans.Lines.Clear;
Memo_Ans.Lines.Add( ansStr );
end;
end.
Delphi FMX Android run time permission sample project demo
unit PMUnit;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, System.Permissions,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
procedure DisplayRationale(Sender: TObject; const APermissions: TClassicStringDynArray; const APostRationaleProc: TProc);
procedure Loacation_PermissionRequestResult(Sender: TObject; const APermissions: TClassicStringDynArray;
const AGrantResults: TClassicPermissionStatusDynArray);
procedure Call_PermissionRequestResult(Sender: TObject; const APermissions: TClassicStringDynArray;
const AGrantResults: TClassicPermissionStatusDynArray);
procedure Camera_PermissionRequestResult(Sender: TObject; const APermissions: TClassicStringDynArray;
const AGrantResults: TClassicPermissionStatusDynArray);
{ Private declarations }
public
{ Public declarations }
FPermissionLoacation, FPermissionCall, FPermissionCamera : string;
end;
var
Form1: TForm1;
implementation
uses
{$IFDEF ANDROID}
Androidapi.JNI.Os,
Androidapi.Helpers,
AndroidApi.Jni.JavaTypes,
FMX.DialogService;
{$ENDIF}
{$R *.fmx}
procedure TForm1.FormCreate(Sender: TObject);
begin
FPermissionLoacation := JStringToString(TJManifest_permission.JavaClass.ACCESS_FINE_LOCATION );
FPermissionCall := JStringToString(TJManifest_permission.JavaClass.CALL_PHONE );
FPermissionCamera := JStringToString(TJManifest_permission.JavaClass.CAMERA );
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
PermissionsService.RequestPermissions([FPermissionLoacation], Loacation_PermissionRequestResult, DisplayRationale);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
PermissionsService.RequestPermissions([FPermissionCall], Call_PermissionRequestResult, DisplayRationale);
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
PermissionsService.RequestPermissions([FPermissionCamera], Camera_PermissionRequestResult, DisplayRationale);
end;
procedure TForm1.DisplayRationale(Sender: TObject; const APermissions: TClassicStringDynArray; const APostRationaleProc: TProc);
var
I: Integer;
RationaleMsg: string;
begin
for I := 0 to High(APermissions) do
begin
if APermissions[I] = FPermissionLoacation then
RationaleMsg := RationaleMsg + 'The app needs to access the Permission Location' + SLineBreak + SLineBreak
else if APermissions[I] = FPermissionCall then
RationaleMsg := RationaleMsg + 'The app needs to access the Permission Call' + SLineBreak + SLineBreak
else if APermissions[I] = FPermissionCamera then
RationaleMsg := RationaleMsg + 'The app needs to access the Permission Camera';
end;
// Show an explanation to the user *asynchronously* - don't block this thread waiting for the user's response!
// After the user sees the explanation, invoke the post-rationale routine to request the permissions
TDialogService.ShowMessage(RationaleMsg,
procedure(const AResult: TModalResult)
begin
APostRationaleProc;
end)
end;
procedure TForm1.Loacation_PermissionRequestResult(Sender: TObject; const APermissions: TClassicStringDynArray; const AGrantResults: TClassicPermissionStatusDynArray);
begin
// 3 permissions involved: CAMERA, READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE
// if (Length(AGrantResults) = 3) and
// (AGrantResults[0] = TPermissionStatus.Granted) and
// (AGrantResults[1] = TPermissionStatus.Granted) and
// (AGrantResults[2] = TPermissionStatus.Granted) then
if ( Length(AGrantResults) = 1) and
(AGrantResults[0] = TPermissionStatus.Granted) then
TDialogService.ShowMessage('Location permissions OK ' )
else
TDialogService.ShowMessage('The required permissions are not granted');
end;
procedure TForm1.Call_PermissionRequestResult(Sender: TObject; const APermissions: TClassicStringDynArray; const AGrantResults: TClassicPermissionStatusDynArray);
begin
if ( Length(AGrantResults) = 1) and
(AGrantResults[0] = TPermissionStatus.Granted) then
TDialogService.ShowMessage('Call permissions OK ' )
else
TDialogService.ShowMessage('The required permissions are not granted');
end;
procedure TForm1.Camera_PermissionRequestResult(Sender: TObject; const APermissions: TClassicStringDynArray; const AGrantResults: TClassicPermissionStatusDynArray);
begin
if ( Length(AGrantResults) = 1) and
(AGrantResults[0] = TPermissionStatus.Granted) then
TDialogService.ShowMessage('Camera permissions OK ' )
else
TDialogService.ShowMessage('The required permissions are not granted');
end;
end.
[FMX] Firemonkey delphi android beep sound
Uses
Androidapi.Helpers,
Androidapi.JNIBridge,
Androidapi.JNI.Media,
AndroidApi.Jni.JavaTypes,
AndroidApi.Jni.App;
procedure BeepSound();
{$IFDEF ANDROID}
var
AudioObj: JObject;
Audio: JAudioManager;
{$ENDIF}
begin
{$IFDEF ANDROID}
AudioObj:= TAndroidHelper.Activity.getSystemService( TJActivity.JavaClass.AUDIO_SERVICE);
Audio := TJAudioManager.Wrap((AudioObj as ILocalObject).GetObjectID);
Audio.loadSoundEffects;
Audio.playSoundEffect( 8 ); // 0 ~ 9
{$ENDIF}
end;
[TMS] Introduction of TAdvStringGrid function - How to link to cell and use balloon help
TAdvStringGrid 의 cell 항목에 외부링크를 연결하여 웹브라우저를 호출 할 수 있고
[TMS] TAdvStringGrid function introduction - How to specify cell item data color

TAdvStringGrid에 입력된 데이터 종류에 따라 폰트 색상을 지정 하는 방법에 대한 샘플 입니다.
값을 양수와 음수로 구별하여 각기 다른 색상을 지정 하게 하였고 앱이 실행된 런타임 상태에서도 색상을 변경 할 수 있게 합니다.
TAdvStringGrid 의 OnGetCellColor 메소드가 사용 되었습니다.
procedure TForm1.Button1Click(Sender: TObject);
var
i, j: Integer;
begin
for i := 1 to AdvStringGrid1.RowCount - 1 do
for j := 1 to AdvStringGrid1.ColCount - 1 do
AdvStringGrid1.Ints[j, i] := Random(1000) - 500;
end;
procedure TForm1.AdvStringGrid1GetCellColor(Sender: TObject; ARow, ACol: Integer; AState: TGridDrawState; ABrush: TBrush; AFont: TFont);
begin
if AdvStringGrid1.Cells[ACol, ARow] <> '' then
if AdvStringGrid1.Ints[ACol, ARow] < 0 then
begin
ABrush.Color := ColorGrid2.BackgroundColor;
AFont.Color := ColorGrid2.ForegroundColor;
end
else
begin
Abrush.Color := Colorgrid1.BackgroundColor;
AFont.Color := Colorgrid1.ForegroundColor;
AFont.Style := [fsBold];
end;
end;
procedure TForm1.ColorGrid1Change(Sender: TObject);
begin
AdvStringGrid1.Invalidate;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Button1Click(Sender);
end;
[TMS] TAdvStringGrid function introduction - csv file import and sorting
TMS 의 TAdvStringGrid 에서 LoadFromCSV 메소드를 사용하면 csv 파일을 불러 올 수 있습니다.
Grid 의 Column 과 Row 는 csv 데이터 항목 수에 맞게 자동으로 세팅 됩니다.
컬럼 헤드 클릭시 Sorting 명령을 수행 하기 위해서 아래와 같이 설정 힙니다.
AdvStringGrid1.SortSettings.Show := TRUE;
Sorting 관련해서 다양한 옵션들이 제공 됩니다. 상세 기능은 세부 메뉴얼을 참고 하시기 바랍니다.
dosort := acol > 0;
procedure TMvForm.Button1Click(Sender: TObject); begin AdvStringGrid1.LoadFromCSV( 'c:\temp\cdata.csv' ); AdvStringGrid1.SortSettings.Show := TRUE; AdvStringGrid1.ColWidths[ 2 ] := 100; end; procedure TMvForm.AdvStringGrid1CanSort(Sender: TObject; ACol: Integer; var DoSort: Boolean); begin dosort := acol > 0; Cursor := crHourGlass; end; procedure TMvForm.AdvStringGrid1ClickSort(Sender: TObject; ACol: Integer); begin Cursor := crDefault; end; procedure TMvForm.AdvStringGrid1GetFormat(Sender: TObject; ACol: Integer; var AStyle: TSortStyle; var aPrefix, aSuffix: string); begin case acol of 1: AStyle := ssAlphabetic; // ssAlphanocase; 2: AStyle := ssNumeric; 3: AStyle := ssNumeric; 4: AStyle := ssDate; end; end;
How to create MDI Form using BPL and FireDAC of one connection with Rad Studio Delphi 11.x
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: 으로 지정 (상세내용 샘플 프로젝트 참조)