![](https://en.delphipraxis.net/uploads/set_resources_2/84c1e40ea0e759e3f1505eb1788ddf3c_pattern.png)
![](https://en.delphipraxis.net/uploads/monthly_2022_01/D_member_7649.png)
DavidJr.
Members-
Content Count
46 -
Joined
-
Last visited
Community Reputation
1 NeutralRecent Profile Visitors
The recent visitors block is disabled and is not being shown to other users.
-
I have a VCL app and I am able to draw cross sections at any specific Z height using SDL Components (RChart) just fine, but I need to view solid objects. Some of the methods here are resused from that app.
-
Hi, with verified (using 3rd party software) 3MF files I am able to load the data, however the TModel3D even with TLight I am just getting a blank window with only the TButton. What am I missing? I have attache pretty much everything I have. David Unit1.pas Unit1.fmx GLCADViewer.dpr GLCADViewer.dproj
-
Thanks. I will. For the time being I am using CreateProcess and calling the script as a commandline and its working fine. I will revisit this later.
-
I did not, I thought that was actually done using the ThreadPythonExec as well as in this: FTask := TTask.Create( procedure var Py: IPyEngineAndGIL; begin Py := SafePyEngine; Py.PythonEngine.ExecStrings(Script); end); FTask.Start; So no matter what I am required to call Py_Begin_Allow_Threads and Py_End_Allow_Threads?
-
I have attached the version of the project that uses the example (to the best of my understanding) ThreadPythonExec: This snippet: // ThreadPythonExec( // procedure // begin // PythonEngine1.ExecStrings(Script); // end, // procedure // begin // FScriptRunning := False; // end, // False); PythonEngine1.ExecStrings(Script); I commented out the ThreadPythonExec and uncommented (the last) line.. and it works fine, but the python app blocks the main UI and I am trying to avoid this. GLDelphiScanner_BKUP.zip
-
Using the "ThreadPythonExec" method, I tried this: unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, System.Threading, System.Math, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, PythonEngine, PythonVersions, ShellAPI, SDL_NumIO, SDL_stringl, Vcl.ExtCtrls, LJUDDelphi, Vcl.WinXCtrls, TlHelp32; const PYHARV_HOME = 'C:\Users\printeruser\Desktop\3DScan_Work\photoneo-python-examples\GigEV\harvesters'; VENV_PATH = PYHARV_HOME + '\.venv'; VENV_PYTHON_EXE = VENV_PATH + '\Scripts\python.exe'; PHOTONEO_GENICAM_PATH = PYHARV_HOME + '\advanced\photoneo_genicam'; LJ_TIMER_INTERVAL = 100; SCAN_MODE_MANUAL = 0; SCAN_MODE_TIMER = 1; SCAN_MODE_RELAY = 2; type TForm1 = class(TForm) Panel1: TPanel; nioVerticalRotation: TNumIO2; PythonInputOutput1: TPythonInputOutput; PythonEngine1: TPythonEngine; RadioGroup1: TRadioGroup; editDevSerialNumber: TEdit; nioHorizontalRotation: TNumIO2; nioZoomFactor: TNumIO2; btnScanNow: TButton; lblDeviceSerialNum_Label: TLabel; Timer1: TTimer; nioTimerInterval: TNumIO2; lblLabJackStatus: TLabel; uiRelay_0_Voltage: TLabel; uiRelay_1_Voltage: TLabel; procedure btnScanNowClick(Sender: TObject); procedure FormActivate(Sender: TObject); procedure FormCreate(Sender: TObject); procedure RadioGroup1Click(Sender: TObject); procedure nioTimerIntervalChange(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure nioZoomFactorChange(Sender: TObject); procedure Panel1DblClick(Sender: TObject); private { Private declarations } // LabJack Vars for the class: FLabJackConnected: Boolean; FRelayMonitorLatch: Boolean; FLJRelay_0_ON, FLJRelay_1_ON: Boolean; FLJErrorcode: LJ_ERROR; // LabJack Error Code FLJHandle: LJ_HANDLE; // Handle for LabJack device FLJVoltage_0, FLJVoltage_1 : double; // Timer & TDateTime Trigger: LastTriggerTime: TDateTime; // The rest of th eclass vars: FTimerTriggeredScan: Boolean; FRelayTriggeredScan: Boolean; FTimeTrigScanInterval: Integer; FScanZoomFactor: Double; FScriptRunning: Boolean; FPythonOpen3DPID: DWORD; PyVersion: TPythonVersion; procedure SetEnvironmentVariables; procedure ConfigurePythonEnvironment; function FindPythonOpen3DSubprocess: DWORD; function StopPythonScript: Boolean; function RunPythonScript: Boolean; procedure RequestElevationIfNeeded; function IsUserAnAdmin: Boolean; // Trigger procedures: procedure TriggerByUserToScan; procedure TriggerByTimerToScan; procedure TriggerByRelayToScan; // LabJack: procedure Initialize_U3HV; procedure ErrorHandler(LJErrorcode: LJ_ERROR; LJIteration: longint); procedure SetDACVoltage(Channel: Integer; TheSetVoltage: Double); procedure GetAnalogVoltage(Channel: Integer; var TheVoltage: Double); public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} // LabJack: procedure TForm1.Initialize_U3HV; var numChannels, quickSample, longSettling: longint; LabJack_Check : integer; begin // This performs the standard initialization of the U3-HV system // Some initial values LabJack_Check := 0; FLJHandle := 0; numChannels := 16; //Number of AIN channels, 0-16. quickSample := 0; //Set to TRUE for quick AIN sampling. longSettling := 1; //Set to TRUE for extra AIN settling time. // Now let's open the labjack FLJErrorcode := OpenLabJack(LJ_dtU3, LJ_ctUSB, '1', 1, FLJHandle); ErrorHandler(FLJErrorcode, 0); if FLJErrorcode > 0 then INC(LabJack_Check); // Now let's reset the configuration FLJErrorcode := ePut(FLJHandle, LJ_ioPIN_CONFIGURATION_RESET, 0, 0, 0); ErrorHandler(FLJErrorcode, 0); if FLJErrorcode > 0 then INC(LabJack_Check); //Configure quickSample. FLJErrorcode := ePut(FLJHandle, LJ_ioPUT_CONFIG, LJ_chAIN_RESOLUTION, quickSample, 0); ErrorHandler(FLJErrorcode, 0); if FLJErrorcode > 0 then INC(LabJack_Check); //Configure longSettling. FLJErrorcode := ePut(FLJHandle, LJ_ioPUT_CONFIG, LJ_chAIN_SETTLING_TIME, longSettling, 0); ErrorHandler(FLJErrorcode, 0); if FLJErrorcode > 0 then INC(LabJack_Check); //Configure the lines as analog. FLJErrorcode := ePut(FLJHandle, LJ_ioPUT_ANALOG_ENABLE_PORT, 0, (IntPower(2, numChannels) - 1), numChannels); ErrorHandler(FLJErrorcode, 0); if FLJErrorcode > 0 then INC(LabJack_Check); if LabJack_Check = 0 then begin lblLabJackStatus.Font.Color := clLime; lblLabJackStatus.Caption := 'LabJack Connected'; end else begin lblLabJackStatus.Font.Color := clRed; lblLabJackStatus.Caption := 'LabJack Disonnected!'; end; // Let's set the DAC0 and DAC1 to 0.0 Volts at boot-up SetDACVoltage(0, 0.0); SetDACVoltage(1, 0.0); end; procedure TForm1.ErrorHandler(LJErrorcode: LJ_ERROR; LJIteration: longint); var err: array[0..254] of AnsiChar; begin if LJErrorcode <> LJE_NOERROR then begin ErrorToString(LJErrorcode, @err); //StatusUpdate('Error number = ' + IntToStr(lngErrorcode)+ ' Error string = ' + string(err) + ' Iteration = ' + IntToStr(LJIteration)); FLabJackConnected := False; // there was an error! end else begin // LabJack Operation is OK FLabJackConnected := True; end; end; procedure TForm1.SetDACVoltage(Channel: Integer; TheSetVoltage: Double); begin if FLabJackConnected then begin // This uses the global lngHandle to refer to the U3-HV if ABS(TheSetVoltage) < 10 then ePUT(FLJHandle, LJ_ioPUT_DAC, Channel, TheSetVoltage, 0) else begin // Add an error statement if the DAC is set overrange...! lblLabJackStatus.Font.Color := clRed; lblLabJackStatus.Caption := 'LabJack Connected & Overrange!'; end; end else begin lblLabJackStatus.Font.Color := clRed; lblLabJackStatus.Caption := 'LabJack Disonnected!'; FLabJackConnected := False; ShowMessage('Problem connecting to LabJack! Check Power and USB connection.'); end; end; procedure TForm1.GetAnalogVoltage(Channel: Integer; var TheVoltage: Double); begin // This uses the global lngHandle to read a voltage from the Analog Channels // // The "LJ_ioGET_AIN" is the integer that determines the operation. Refer to // LJUDDelphi.pas for details if FLabJackConnected then begin eGET(FLJHandle, LJ_ioGET_AIN, Channel, TheVoltage, 0); end else begin lblLabJackStatus.Font.Color := clRed; lblLabJackStatus.Caption := 'LabJack Disonnected!'; FLabJackConnected := False; ShowMessage('Problem connecting to LabJack! Check Power and USB connection.'); end; // Update the Text readings if Channel = 0 then uiRelay_0_Voltage.Caption := 'Relay 0 Voltage = '+ Strff(FLJVoltage_0, 4, 3)+' V'; if Channel = 1 then uiRelay_1_Voltage.Caption := 'Relay 1 Voltage = '+ Strff(FLJVoltage_1, 4, 3)+' V'; end; // The Rest of the Code: function TForm1.IsUserAnAdmin: Boolean; var hToken: THandle; Elevation: TOKEN_ELEVATION; cbSize: DWORD; begin Result := False; if OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, hToken) then try cbSize := SizeOf(TOKEN_ELEVATION); if GetTokenInformation(hToken, TokenElevation, @Elevation, cbSize, cbSize) then Result := Elevation.TokenIsElevated <> 0; finally CloseHandle(hToken); end; end; procedure TForm1.RequestElevationIfNeeded; var sei: TShellExecuteInfo; exeName: string; begin if not IsUserAnAdmin then begin ZeroMemory(@sei, SizeOf(sei)); sei.cbSize := SizeOf(sei); sei.Wnd := Handle; // Parent window sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI; sei.lpVerb := 'runas'; // Request elevation exeName := ParamStr(0); sei.lpFile := PChar(exeName); sei.lpParameters := PChar(''); // No parameters sei.nShow := SW_SHOWNORMAL; if not ShellExecuteEx(@sei) then begin ShowMessage('Failed to create elevated process. Error code: ' + IntToStr(GetLastError)); Application.Terminate; // Terminate the unelevated instance end else Application.Terminate; // Terminate the unelevated instance end; end; procedure TForm1.SetEnvironmentVariables; begin SetEnvironmentVariable('VIRTUAL_ENV', PChar(VENV_PATH)); SetEnvironmentVariable('PYTHONHOME', nil); // Unset PYTHONHOME SetEnvironmentVariable('PATH', PChar(VENV_PATH + '\Scripts;' + GetEnvironmentVariable('PATH'))); end; procedure TForm1.ConfigurePythonEnvironment; begin MaskFPUExceptions(True); // Ensure the virtual environment paths are correctly set SetEnvironmentVariables; // Configure Python environment PythonEngine1.ExecString(Format( 'import sys; import os; ' + 'sys.path.append(r"%s"); ' + 'sys.path.append(r"%s\Lib\site-packages"); ' + 'sys.path.append(r"%s"); ' + // Add path to photoneo_genicam 'os.environ["VIRTUAL_ENV"] = r"%s"; ' + 'os.environ["PATH"] = r"%s\Scripts;" + os.environ["PATH"]; ' + 'print("sys.path:", sys.path); ' + 'print("VIRTUAL_ENV:", os.environ["VIRTUAL_ENV"]); ' + 'print("PATH:", os.environ["PATH"])', [VENV_PATH, VENV_PATH, PHOTONEO_GENICAM_PATH, VENV_PATH, VENV_PATH] )); end; procedure TForm1.nioTimerIntervalChange(Sender: TObject); begin try if(Not nioTimerInterval.Value.IsNan) then FTimeTrigScanInterval := nioTimerInterval.IntegerValue; except FTimeTrigScanInterval := 10; nioTimerInterval.Value := FTimeTrigScanInterval; end; end; procedure TForm1.nioZoomFactorChange(Sender: TObject); begin try if(Not nioZoomFactor.Value.IsNan) then FScanZoomFactor := (100 / nioZoomFactor.IntegerValue); except FScanZoomFactor := 0.3; nioTimerInterval.Value := (100/FScanZoomFactor); end; end; procedure TForm1.Panel1DblClick(Sender: TObject); begin // Reset fields: if(GetKeyState(VK_SHIFT) < 0) then begin nioHorizontalRotation.Value := 360; nioVerticalRotation.Value := 180; nioZoomFactor.Value := 200; FScanZoomFactor := (100 / nioZoomFactor.IntegerValue); end; end; procedure TForm1.FormCreate(Sender: TObject); begin RequestElevationIfNeeded; FTimerTriggeredScan := False; FRelayTriggeredScan := False; FScriptRunning := False; Timer1.Interval := LJ_TIMER_INTERVAL; // Load the Python DLL using the virtual environment's Python executable try FTimeTrigScanInterval := nioTimerInterval.IntegerValue; FScanZoomFactor := (100 / nioZoomFactor.IntegerValue); if PythonVersionFromPath(PYHARV_HOME + '\.venv\', PyVersion) then begin PythonEngine1.VenvPythonExe := VENV_PYTHON_EXE; PythonEngine1.SetPythonHome(PYHARV_HOME + '\.venv\'); PyVersion.AssignTo(PythonEngine1); PythonEngine1.LoadDll; // ShowMessage('Python DLL loaded successfully. ' + PythonEngine1.DllName); ConfigurePythonEnvironment; end else begin ShowMessage('No vEnv found!'); end; except on E: Exception do ShowMessage('Failed to load Python DLL: ' + E.Message); end; end; procedure TForm1.FormActivate(Sender: TObject); begin if(Not DirectoryExists(PYHARV_HOME)) then begin ShowMessage(PYHARV_HOME + ' doesn''t exist!'); end; FTimeTrigScanInterval := nioTimerInterval.IntegerValue; LastTriggerTime := Now; // try to connect to Lab Jack: Initialize_U3HV; // Now Check if its connected: if(Not FLabJackconnected) then begin lblLabJackStatus.Font.Color := clRed; lblLabJackStatus.Caption := 'LabJack Disonnected!'; end; end; procedure TForm1.RadioGroup1Click(Sender: TObject); begin case RadioGroup1.ItemIndex of SCAN_MODE_MANUAL: begin FTimerTriggeredScan := False; FRelayTriggeredScan := False; btnScanNow.Enabled := (Not FTimerTriggeredScan) AND (Not FRelayTriggeredScan); nioTimerInterval.Enabled := FTimerTriggeredScan; end; SCAN_MODE_TIMER: begin FTimerTriggeredScan := True; FRelayTriggeredScan := False; btnScanNow.Enabled := (Not FTimerTriggeredScan) AND (Not FRelayTriggeredScan); nioTimerInterval.Enabled := FTimerTriggeredScan; end; SCAN_MODE_RELAY: begin FTimerTriggeredScan := False; FRelayTriggeredScan := True; btnScanNow.Enabled := (Not FTimerTriggeredScan) AND (Not FRelayTriggeredScan); nioTimerInterval.Enabled := FTimerTriggeredScan; end; else begin RadioGroup1.ItemIndex := 0; btnScanNow.Enabled := True; nioTimerInterval.Enabled := False; end; end; end; procedure TForm1.btnScanNowClick(Sender: TObject); begin btnScanNow.Enabled := False; if FScriptRunning then begin ShowMessage('Scan is still busy.'); Exit; end; Application.ProcessMessages; if(Tbutton(Sender).Name = 'btnScanNow') then begin TTask.Run( procedure var t: Integer; begin for t := 0 to 9000 do begin Sleep(10); end; TThread.Synchronize(nil, procedure begin //Py_Exit PythonEngine1.ExecString('exit(0)'); Application.ProcessMessages; end); end); TriggerByUserToScan; end; btnScanNow.Enabled := (Not FScriptRunning); end; procedure TForm1.Timer1Timer(Sender: TObject); begin if(FTimerTriggeredScan OR FRelayTriggeredScan) then begin Timer1.Enabled := False; //Check for Triggers: if(Not FTimerTriggeredScan) then TriggerByRelayToScan; if(Not FRelayTriggeredScan) then TriggerByTimerToScan; Timer1.Enabled := True; end; end; procedure TForm1.TriggerByUserToScan; begin if((Not FTimerTriggeredScan) AND (Not FRelayTriggeredScan)) then begin RunPythonScript; end; end; procedure TForm1.TriggerByTimerToScan; var myCurrentTime: TDateTime; myTriggerTimeElapsed: Double; begin myCurrentTime := Now; // Calculate time elapsed since the last execution of UpdateMassChart myTriggerTimeElapsed := (myCurrentTime - LastTriggerTime) * 86400; // Convert to seconds if(myTriggerTimeElapsed >= FTimeTrigScanInterval) then begin StopPythonScript; LastTriggerTime := myCurrentTime; RunPythonScript; end; end; procedure TForm1.TriggerByRelayToScan; var ActiveFileName : String; myActiveFileCheckSum: String; begin // Enable the Print Monitor Button if(FLabJackConnected AND FRelayTriggeredScan AND (Not FTimerTriggeredScan)) then begin // After the first read.....The Monitorlatch is FALSE....it should only be true // again if BOTH voltages are less than 2.0V!!!! // Just get the voltage readings from the Labjack GetAnalogVoltage(0, FLJVoltage_0); GetAnalogVoltage(1, FLJVoltage_1); if ((FLJVoltage_0 < 2.0) and (FLJVoltage_1 < 2.0)) then FRelayMonitorLatch := True; // Update the Relay Lights Status if(FLJVoltage_0 > 2.0) then begin // Use 2V as the trigger voltage level for now //uiRelay_0_Light.Fill.Color := TAlphaColorRec.Lime; FLJRelay_0_ON := True; end else begin //uiRelay_0_Light.Fill.Color := TAlphaColorRec.Crimson; FLJRelay_0_ON := False; end; if(FLJVoltage_1 > 2.0) then begin // Use 2V as the trigger voltage level for now //uiRelay_1_Light.Fill.Color := TAlphaColorRec.Lime; FLJRelay_1_ON := True; end else begin //uiRelay_1_Light.Fill.Color := TAlphaColorRec.Crimson; FLJRelay_1_ON := False; end; // Now let's figure out if we need to take a photo // // The camera will be "inactive" most of the time // Make it active first.... // // then wait for Relay_1 to turn ON // // Take the photo and turn Relay 1 OFF and Turn Relay 0 OFF // if FLJRelay_0_ON then begin // This means the camera is "active" and updating images // // Now let's see if Relay 1 is ON if FLJRelay_1_ON then begin // Here you should execute scan! // if FRelayMonitorLatch then begin StopPythonScript; // Now let's write the file! RunPythonScript; end; // Now set the MonitorLatch to False! FRelayMonitorLatch := False; end; end; end; end; // Python uses Open3D Library modules: function TForm1.FindPythonOpen3DSubprocess: DWORD; var Snapshot: THandle; ProcessEntry: TProcessEntry32; begin Result := 0; Snapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if Snapshot <> INVALID_HANDLE_VALUE then try ProcessEntry.dwSize := SizeOf(ProcessEntry); if Process32First(Snapshot, ProcessEntry) then begin repeat if Pos('Open3D', ProcessEntry.szExeFile) > 0 then begin Result := ProcessEntry.th32ProcessID; Break; end; until Not Process32Next(Snapshot, ProcessEntry); end; finally CloseHandle(Snapshot); end; end; // Terminate Python execution: function TForm1.StopPythonScript: Boolean; procedure TerminateProcessByPID(PID: DWORD); var ProcessHandle: THandle; begin ProcessHandle := OpenProcess(PROCESS_TERMINATE, False, PID); if ProcessHandle <> 0 then try TerminateProcess(ProcessHandle, 0); finally CloseHandle(ProcessHandle); end; end; begin Result := False; // Terminate the Open3D subprocess if it exists FPythonOpen3DPID := FindPythonOpen3DSubprocess; if FPythonOpen3DPID <> 0 then TerminateProcessByPID(FPythonOpen3DPID); end; // The Actual Python execution: function TForm1.RunPythonScript: Boolean; var Script: TStringList; ScriptFilePath, DeviceID, HorizontalRotation, VerticalRotation, ZoomFactor: string; begin // Prevent Double Clicking... since the python script needs to complete first before re-running: FScriptRunning := True; Script := TStringList.Create; try // Retrieve the values from the form inputs DeviceID := editDevSerialNumber.Text; HorizontalRotation := nioHorizontalRotation.Value.ToString; VerticalRotation := nioVerticalRotation.Value.ToString; ZoomFactor := FScanZoomFactor.ToString; ScriptFilePath := PYHARV_HOME + '\advanced\GridLoigic_pointcloud_with_normals_and_texture.py'; if FileExists(ScriptFilePath) then begin Script.LoadFromFile(ScriptFilePath); // Insert the sys.path and debug statements Script.Insert(2, 'sys.path.append(r"' + PYHARV_HOME + '\advanced")'); Script.Insert(3, 'print("Starting script execution")'); Script.Insert(4, 'try:'); Script.Insert(5, ' import photoneo_genicam'); Script.Insert(6, ' print("photoneo_genicam imported successfully")'); Script.Insert(7, 'except ImportError as e:'); Script.Insert(8, ' print("Failed to import photoneo_genicam:", str(e))'); Script.Insert(9, ' raise'); // Add the parameters as comments for reference Script.Insert(10, '# Parameters passed from Delphi'); Script.Insert(11, 'device_id = "' + DeviceID + '"'); Script.Insert(12, 'horizontal_angle = ' + HorizontalRotation); Script.Insert(13, 'vertical_angle = ' + VerticalRotation); Script.Insert(14, 'zoom_factor = ' + ZoomFactor); // Replace main function call with parameters //Script.Add('main(device_id, horizontal_angle, vertical_angle, zoom_factor)'); try ThreadPythonExec( procedure begin GetPythonEngine.ExecStrings(Script); end, procedure begin FScriptRunning := False; end, False); //PythonEngine1.ExecStrings(Script); //ShowMessage('Script executed successfully.'); except on E: EPySystemExit do begin // Handle the SystemExit exception ShowMessage('Script terminated by SystemExit.'); end; on E: EPythonError do begin ShowMessage('Python Error: ' + E.Message); end; on E: Exception do begin ShowMessage('Error: ' + E.Message); end; end; end else begin ShowMessage('Script file does not exist: ' + ScriptFilePath); end; finally Script.Free; //PythonEngine1. //if(Not (FTimerTriggeredScan or FRelayTriggeredScan)) then //FScriptRunning := False; end; end; end.
-
Hi, I am trying to call a Python (with Open3D) app UI from a Delphi form (11.3), using a Tthread .. it seems to call the method just fine but I do not see the window. When not using it in a thread the window comes up just fine... however I want to be able to terminate the process at will (from within the Delphi Form). What do I need to do in order to make the python app be visible? (Note: I am also using SDL Components for the input fields that handle numbers.) unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, System.Threading, System.Math, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, PythonEngine, PythonVersions, ShellAPI, SDL_NumIO, SDL_stringl, Vcl.ExtCtrls, LJUDDelphi, Vcl.WinXCtrls, TlHelp32; const PYHARV_HOME = 'C:\Users\printeruser\Desktop\3DScan_Work\photoneo-python-examples\GigEV\harvesters'; VENV_PATH = PYHARV_HOME + '\.venv'; VENV_PYTHON_EXE = VENV_PATH + '\Scripts\python.exe'; PHOTONEO_GENICAM_PATH = PYHARV_HOME + '\advanced\photoneo_genicam'; LJ_TIMER_INTERVAL = 100; SCAN_MODE_MANUAL = 0; SCAN_MODE_TIMER = 1; SCAN_MODE_RELAY = 2; // THREAD EVENT_ID_NUMS: CloseScanResultEventID = 0; RunScanGetResultEventID = 1; type TPythonScriptThread = class(TThread) private FPythonEngine: TPythonEngine; FPythonInputOutput: TPythonInputOutput; FPyVersion: TPythonVersion; FScript: TStringList; FDevSerialNumber: String; FHorizontalRotation: Double; FVerticalRotation: Double; FScanZoomFactor: Double; FScriptRunning: Boolean; FPythonOpen3DPID: DWORD; FCloseScanResultEvent: THandle; FRunScanGetResultEvent: THandle; procedure SetEnvironmentVariables; procedure ConfigurePythonEnvironment; procedure Set_DevSerialNumber(Value: String); procedure Set_HorizontalRotation(Value: Double); procedure Set_VerticalRotation(Value: Double); procedure Set_ScanZoomFactor(Value: Double); // Thread Event triggered methods: function _ExecuteStopPythonScript: Boolean; function _ExecuteRunPythonScript: Boolean; // Private Thread member methods that are called by UI invoking a thread-safe event method: function mFindPythonOpen3DSubprocess: DWORD; protected procedure Execute; override; public constructor Create; destructor Destroy; override; // Read Only Property: property ScriptRunning: Boolean read FScriptRunning; // The publicly accessible methods: function StopPythonScript: Boolean; function RunPythonScript(DevID: String; HRot: Double = 0; VRot: Double = 0; Zoom: Double = 1): Boolean; end; TForm1 = class(TForm) Panel1: TPanel; nioVerticalRotation: TNumIO2; PythonEngine1: TPythonEngine; RadioGroup1: TRadioGroup; editDevSerialNumber: TEdit; nioHorizontalRotation: TNumIO2; nioZoomFactor: TNumIO2; btnScanNow: TButton; lblDeviceSerialNum_Label: TLabel; Timer1: TTimer; nioTimerInterval: TNumIO2; lblLabJackStatus: TLabel; uiRelay_0_Voltage: TLabel; uiRelay_1_Voltage: TLabel; procedure btnScanNowClick(Sender: TObject); procedure FormActivate(Sender: TObject); procedure FormCreate(Sender: TObject); procedure RadioGroup1Click(Sender: TObject); procedure nioTimerIntervalChange(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure nioZoomFactorChange(Sender: TObject); procedure Panel1DblClick(Sender: TObject); private { Private declarations } PythonScriptThread: TPythonScriptThread; // LabJack Vars for the class: FLabJackConnected: Boolean; FRelayMonitorLatch: Boolean; FLJRelay_0_ON, FLJRelay_1_ON: Boolean; FLJErrorcode: LJ_ERROR; // LabJack Error Code FLJHandle: LJ_HANDLE; // Handle for LabJack device FLJVoltage_0, FLJVoltage_1 : double; // Timer & TDateTime Trigger: LastTriggerTime: TDateTime; // The rest of th eclass vars: FTimerTriggeredScan: Boolean; FRelayTriggeredScan: Boolean; FTimeTrigScanInterval: Integer; procedure RequestElevationIfNeeded; function IsUserAnAdmin: Boolean; // Trigger procedures: procedure TriggerByUserToScan; procedure TriggerByTimerToScan; procedure TriggerByRelayToScan; // LabJack: procedure Initialize_U3HV; procedure ErrorHandler(LJErrorcode: LJ_ERROR; LJIteration: longint); procedure SetDACVoltage(Channel: Integer; TheSetVoltage: Double); procedure GetAnalogVoltage(Channel: Integer; var TheVoltage: Double); public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} // The Rest of the Code: function TForm1.IsUserAnAdmin: Boolean; var hToken: THandle; Elevation: TOKEN_ELEVATION; cbSize: DWORD; begin Result := False; if OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, hToken) then try cbSize := SizeOf(TOKEN_ELEVATION); if GetTokenInformation(hToken, TokenElevation, @Elevation, cbSize, cbSize) then Result := Elevation.TokenIsElevated <> 0; finally CloseHandle(hToken); end; end; procedure TForm1.RequestElevationIfNeeded; var sei: TShellExecuteInfo; exeName: string; begin if not IsUserAnAdmin then begin ZeroMemory(@sei, SizeOf(sei)); sei.cbSize := SizeOf(sei); sei.Wnd := Handle; // Parent window sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI; sei.lpVerb := 'runas'; // Request elevation exeName := ParamStr(0); sei.lpFile := PChar(exeName); sei.lpParameters := PChar(''); // No parameters sei.nShow := SW_SHOWNORMAL; if not ShellExecuteEx(@sei) then begin ShowMessage('Failed to create elevated process. Error code: ' + IntToStr(GetLastError)); Application.Terminate; // Terminate the unelevated instance end else Application.Terminate; // Terminate the unelevated instance end; end; // LabJack: procedure TForm1.Initialize_U3HV; var numChannels, quickSample, longSettling: longint; LabJack_Check : integer; begin // This performs the standard initialization of the U3-HV system // Some initial values LabJack_Check := 0; FLJHandle := 0; numChannels := 16; //Number of AIN channels, 0-16. quickSample := 0; //Set to TRUE for quick AIN sampling. longSettling := 1; //Set to TRUE for extra AIN settling time. // Now let's open the labjack FLJErrorcode := OpenLabJack(LJ_dtU3, LJ_ctUSB, '1', 1, FLJHandle); ErrorHandler(FLJErrorcode, 0); if FLJErrorcode > 0 then INC(LabJack_Check); // Now let's reset the configuration FLJErrorcode := ePut(FLJHandle, LJ_ioPIN_CONFIGURATION_RESET, 0, 0, 0); ErrorHandler(FLJErrorcode, 0); if FLJErrorcode > 0 then INC(LabJack_Check); //Configure quickSample. FLJErrorcode := ePut(FLJHandle, LJ_ioPUT_CONFIG, LJ_chAIN_RESOLUTION, quickSample, 0); ErrorHandler(FLJErrorcode, 0); if FLJErrorcode > 0 then INC(LabJack_Check); //Configure longSettling. FLJErrorcode := ePut(FLJHandle, LJ_ioPUT_CONFIG, LJ_chAIN_SETTLING_TIME, longSettling, 0); ErrorHandler(FLJErrorcode, 0); if FLJErrorcode > 0 then INC(LabJack_Check); //Configure the lines as analog. FLJErrorcode := ePut(FLJHandle, LJ_ioPUT_ANALOG_ENABLE_PORT, 0, (IntPower(2, numChannels) - 1), numChannels); ErrorHandler(FLJErrorcode, 0); if FLJErrorcode > 0 then INC(LabJack_Check); if LabJack_Check = 0 then begin lblLabJackStatus.Font.Color := clLime; lblLabJackStatus.Caption := 'LabJack Connected'; end else begin lblLabJackStatus.Font.Color := clRed; lblLabJackStatus.Caption := 'LabJack Disonnected!'; end; // Let's set the DAC0 and DAC1 to 0.0 Volts at boot-up SetDACVoltage(0, 0.0); SetDACVoltage(1, 0.0); end; procedure TForm1.ErrorHandler(LJErrorcode: LJ_ERROR; LJIteration: longint); var err: array[0..254] of AnsiChar; begin if LJErrorcode <> LJE_NOERROR then begin ErrorToString(LJErrorcode, @err); //StatusUpdate('Error number = ' + IntToStr(lngErrorcode)+ ' Error string = ' + string(err) + ' Iteration = ' + IntToStr(LJIteration)); FLabJackConnected := False; // there was an error! end else begin // LabJack Operation is OK FLabJackConnected := True; end; end; procedure TForm1.SetDACVoltage(Channel: Integer; TheSetVoltage: Double); begin if FLabJackConnected then begin // This uses the global lngHandle to refer to the U3-HV if ABS(TheSetVoltage) < 10 then ePUT(FLJHandle, LJ_ioPUT_DAC, Channel, TheSetVoltage, 0) else begin // Add an error statement if the DAC is set overrange...! lblLabJackStatus.Font.Color := clRed; lblLabJackStatus.Caption := 'LabJack Connected & Overrange!'; end; end else begin lblLabJackStatus.Font.Color := clRed; lblLabJackStatus.Caption := 'LabJack Disonnected!'; FLabJackConnected := False; ShowMessage('Problem connecting to LabJack! Check Power and USB connection.'); end; end; procedure TForm1.GetAnalogVoltage(Channel: Integer; var TheVoltage: Double); begin // This uses the global lngHandle to read a voltage from the Analog Channels // // The "LJ_ioGET_AIN" is the integer that determines the operation. Refer to // LJUDDelphi.pas for details if FLabJackConnected then begin eGET(FLJHandle, LJ_ioGET_AIN, Channel, TheVoltage, 0); end else begin lblLabJackStatus.Font.Color := clRed; lblLabJackStatus.Caption := 'LabJack Disonnected!'; FLabJackConnected := False; ShowMessage('Problem connecting to LabJack! Check Power and USB connection.'); end; // Update the Text readings if Channel = 0 then uiRelay_0_Voltage.Caption := 'Relay 0 Voltage = '+ Strff(FLJVoltage_0, 4, 3)+' V'; if Channel = 1 then uiRelay_1_Voltage.Caption := 'Relay 1 Voltage = '+ Strff(FLJVoltage_1, 4, 3)+' V'; end; // Python script thread implementation constructor TPythonScriptThread.Create; begin inherited Create(False); FPythonEngine := TPythonEngine.Create(Form1); FPythonInputOutput := TPythonInputOutput.Create(Form1); if PythonVersionFromPath(PYHARV_HOME + '\.venv\', FPyVersion) then begin // Create the Events: FCloseScanResultEvent := CreateEvent(nil, True, False, nil); FRunScanGetResultEvent := CreateEvent(nil, True, False, nil); FPythonEngine.VenvPythonExe := VENV_PYTHON_EXE; FPythonEngine.SetPythonHome(PYHARV_HOME + '\.venv\'); FPyVersion.AssignTo(FPythonEngine); FPythonEngine.LoadDll; // ShowMessage('Python DLL loaded successfully. ' + PythonEngine1.DllName); ConfigurePythonEnvironment; end end; destructor TPythonScriptThread.Destroy; begin FScript.Free; inherited; end; // The publicly accessible methods: function TPythonScriptThread.StopPythonScript: Boolean; begin //CloseScanResultEventID SetEvent(FCloseScanResultEvent); end; function TPythonScriptThread.RunPythonScript(DevID: String; HRot: Double = 0; VRot: Double = 0; Zoom: Double = 1): Boolean; begin Result := False; // Set the Params: FDevSerialNumber := DevID; FHorizontalRotation := HRot; FVerticalRotation := VRot; FScanZoomFactor:= (100 / Zoom); //RunScanGetResultEventID if(FDevSerialNumber <> '') AND (Not FHorizontalRotation.IsNan) AND (Not FVerticalRotation.IsNan) AND (Not FScanZoomFactor.IsNan) then begin SetEvent(FRunScanGetResultEvent); Result := True; end; end; procedure TPythonScriptThread.Execute; var EventHandles: array[0..1] of THandle; EventIndex: DWORD; begin EventHandles[CloseScanResultEventID] := FCloseScanResultEvent; EventHandles[RunScanGetResultEventID] := FRunScanGetResultEvent; while(Not Terminated) do begin EventIndex := WaitForMultipleObjects(2, @EventHandles, False, 60); case EventIndex of WAIT_OBJECT_0 + CloseScanResultEventID: // FJogStopEvent signaled begin // Handle FCloseScanResultEvent signaled case _ExecuteStopPythonScript; ResetEvent(FCloseScanResultEvent); end; WAIT_OBJECT_0 + RunScanGetResultEventID: // FJogStopEvent signaled begin // Handle FRunScanGetResultEvent signaled case _ExecuteRunPythonScript; ResetEvent(FRunScanGetResultEvent); end; end; end; CloseHandle(FCloseScanResultEvent); CloseHandle(FRunScanGetResultEvent); end; // Python uses Open3D Library modules: function TPythonScriptThread.mFindPythonOpen3DSubprocess: DWORD; var Snapshot: THandle; ProcessEntry: TProcessEntry32; begin Result := 0; Snapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if Snapshot <> INVALID_HANDLE_VALUE then try ProcessEntry.dwSize := SizeOf(ProcessEntry); if Process32First(Snapshot, ProcessEntry) then begin repeat if Pos('Open3D', ProcessEntry.szExeFile) > 0 then begin Result := ProcessEntry.th32ProcessID; Break; end; until Not Process32Next(Snapshot, ProcessEntry); end; finally CloseHandle(Snapshot); end; end; function TPythonScriptThread._ExecuteStopPythonScript: Boolean; begin end; // The Actual Python execution: function TPythonScriptThread._ExecuteRunPythonScript: Boolean; var Script: TStringList; ScriptFilePath, HorizontalRotation, VerticalRotation, ZoomFactor: string; begin // Prevent Double Clicking... since the python script needs to complete first before re-running: FScriptRunning := True; Script := TStringList.Create; try // Retrieve the values from the form inputs ScriptFilePath := PYHARV_HOME + '\advanced\GridLoigic_pointcloud_with_normals_and_texture.py'; if FileExists(ScriptFilePath) then begin Script.LoadFromFile(ScriptFilePath); HorizontalRotation := FHorizontalRotation.ToString; VerticalRotation := FVerticalRotation.ToString; ZoomFactor := FScanZoomFactor.ToString; // Insert the sys.path and debug statements Script.Insert(2, 'sys.path.append(r"' + PYHARV_HOME + '\advanced")'); Script.Insert(3, 'print("Starting script execution")'); Script.Insert(4, 'try:'); Script.Insert(5, ' import photoneo_genicam'); Script.Insert(6, ' print("photoneo_genicam imported successfully")'); Script.Insert(7, 'except ImportError as e:'); Script.Insert(8, ' print("Failed to import photoneo_genicam:", str(e))'); Script.Insert(9, ' raise'); // Add the parameters as comments for reference Script.Insert(10, '# Parameters passed from Delphi'); Script.Insert(11, 'device_id = "' + FDevSerialNumber + '"'); Script.Insert(12, 'horizontal_angle = ' + HorizontalRotation); Script.Insert(13, 'vertical_angle = ' + VerticalRotation); Script.Insert(14, 'zoom_factor = ' + ZoomFactor); // Replace main function call with parameters //Script.Add('main(device_id, horizontal_angle, vertical_angle, zoom_factor)'); try FPythonEngine.ExecStrings(Script); //ShowMessage('Script executed successfully.'); except on E: EPySystemExit do begin // Handle the SystemExit exception ShowMessage('Script terminated by SystemExit.'); end; on E: EPythonError do begin ShowMessage('Python Error: ' + E.Message); end; on E: Exception do begin ShowMessage('Error: ' + E.Message); end; end; end else begin ShowMessage('Script file does not exist: ' + ScriptFilePath); end; finally Script.Free; end; end; procedure TPythonScriptThread.SetEnvironmentVariables; begin SetEnvironmentVariable('VIRTUAL_ENV', PChar(VENV_PATH)); SetEnvironmentVariable('PYTHONHOME', nil); // Unset PYTHONHOME SetEnvironmentVariable('PATH', PChar(VENV_PATH + '\Scripts;' + GetEnvironmentVariable('PATH'))); end; procedure TPythonScriptThread.ConfigurePythonEnvironment; begin MaskFPUExceptions(True); // Ensure the virtual environment paths are correctly set SetEnvironmentVariables; // Configure Python environment FPythonEngine.ExecString(Format( 'import sys; import os; ' + 'sys.path.append(r"%s"); ' + 'sys.path.append(r"%s\Lib\site-packages"); ' + 'sys.path.append(r"%s"); ' + // Add path to photoneo_genicam 'os.environ["VIRTUAL_ENV"] = r"%s"; ' + 'os.environ["PATH"] = r"%s\Scripts;" + os.environ["PATH"]; ' + 'print("sys.path:", sys.path); ' + 'print("VIRTUAL_ENV:", os.environ["VIRTUAL_ENV"]); ' + 'print("PATH:", os.environ["PATH"])', [VENV_PATH, VENV_PATH, PHOTONEO_GENICAM_PATH, VENV_PATH, VENV_PATH] )); end; procedure TPythonScriptThread.Set_DevSerialNumber(Value: String); begin FDevSerialNumber := Value; end; procedure TPythonScriptThread.Set_HorizontalRotation(Value: Double); begin FHorizontalRotation := Value; end; procedure TPythonScriptThread.Set_VerticalRotation(Value: Double); begin FVerticalRotation := Value; end; procedure TPythonScriptThread.Set_ScanZoomFactor(Value: Double); begin FScanZoomFactor := Value; end; // Form stuff: procedure TForm1.nioTimerIntervalChange(Sender: TObject); begin try if(Not nioTimerInterval.Value.IsNan) then FTimeTrigScanInterval := nioTimerInterval.IntegerValue; except FTimeTrigScanInterval := 10; nioTimerInterval.Value := FTimeTrigScanInterval; end; end; procedure TForm1.nioZoomFactorChange(Sender: TObject); begin try if(nioZoomFactor.Value.IsNan) AND (Assigned(PythonScriptThread)) then nioTimerInterval.Value := 200; except end; end; procedure TForm1.Panel1DblClick(Sender: TObject); begin // Reset fields: if(GetKeyState(VK_SHIFT) < 0) then begin nioHorizontalRotation.Value := 360; nioVerticalRotation.Value := 180; nioZoomFactor.Value := 200; end; end; procedure TForm1.FormCreate(Sender: TObject); begin RequestElevationIfNeeded; FTimerTriggeredScan := False; FRelayTriggeredScan := False; Timer1.Interval := LJ_TIMER_INTERVAL; end; procedure TForm1.FormActivate(Sender: TObject); begin if(Not DirectoryExists(PYHARV_HOME)) then begin ShowMessage(PYHARV_HOME + ' doesn''t exist!'); end else begin // Load the Python DLL using the virtual environment's Python executable PythonScriptThread := TPythonScriptThread.Create; Sleep(10); Application.ProcessMessages; Sleep(10); Application.ProcessMessages; if(Assigned(PythonScriptThread)) then begin try FTimeTrigScanInterval := nioTimerInterval.IntegerValue; except on E: Exception do ShowMessage('Failed to load Python DLL: ' + E.Message); end; FTimeTrigScanInterval := nioTimerInterval.IntegerValue; LastTriggerTime := Now; // try to connect to Lab Jack: Initialize_U3HV; // Now Check if its connected: if(Not FLabJackconnected) then begin lblLabJackStatus.Font.Color := clRed; lblLabJackStatus.Caption := 'LabJack Disonnected!'; end; end; end; end; procedure TForm1.RadioGroup1Click(Sender: TObject); begin case RadioGroup1.ItemIndex of SCAN_MODE_MANUAL: begin FTimerTriggeredScan := False; FRelayTriggeredScan := False; btnScanNow.Enabled := (Not FTimerTriggeredScan) AND (Not FRelayTriggeredScan); nioTimerInterval.Enabled := FTimerTriggeredScan; end; SCAN_MODE_TIMER: begin FTimerTriggeredScan := True; FRelayTriggeredScan := False; btnScanNow.Enabled := (Not FTimerTriggeredScan) AND (Not FRelayTriggeredScan); nioTimerInterval.Enabled := FTimerTriggeredScan; end; SCAN_MODE_RELAY: begin FTimerTriggeredScan := False; FRelayTriggeredScan := True; btnScanNow.Enabled := (Not FTimerTriggeredScan) AND (Not FRelayTriggeredScan); nioTimerInterval.Enabled := FTimerTriggeredScan; end; else begin RadioGroup1.ItemIndex := 0; btnScanNow.Enabled := True; nioTimerInterval.Enabled := False; end; end; end; procedure TForm1.btnScanNowClick(Sender: TObject); begin btnScanNow.Enabled := False; if PythonScriptThread.ScriptRunning then begin ShowMessage('Scan is still busy.'); Exit; end; Application.ProcessMessages; if(Tbutton(Sender).Name = 'btnScanNow') then begin TriggerByUserToScan; end; btnScanNow.Enabled := (Not PythonScriptThread.ScriptRunning); end; procedure TForm1.Timer1Timer(Sender: TObject); begin if(FTimerTriggeredScan OR FRelayTriggeredScan) then begin Timer1.Enabled := False; //Check for Triggers: if(Not FTimerTriggeredScan) then TriggerByRelayToScan; if(Not FRelayTriggeredScan) then TriggerByTimerToScan; Timer1.Enabled := True; end; end; procedure TForm1.TriggerByUserToScan; begin if((Not FTimerTriggeredScan) AND (Not FRelayTriggeredScan)) then begin if(Not PythonScriptThread.RunPythonScript( editDevSerialNumber.Text, nioHorizontalRotation.Value, nioVerticalRotation.Value, nioZoomFactor.Value )) then begin //Error: ShowMessage('Could not call process!'); end; end; end; procedure TForm1.TriggerByTimerToScan; var myCurrentTime: TDateTime; myTriggerTimeElapsed: Double; begin myCurrentTime := Now; // Calculate time elapsed since the last execution of UpdateMassChart myTriggerTimeElapsed := (myCurrentTime - LastTriggerTime) * 86400; // Convert to seconds if(myTriggerTimeElapsed >= FTimeTrigScanInterval) then begin PythonScriptThread.StopPythonScript; LastTriggerTime := myCurrentTime; PythonScriptThread.RunPythonScript( editDevSerialNumber.Text, nioHorizontalRotation.Value, nioVerticalRotation.Value, nioZoomFactor.Value ); end; end; procedure TForm1.TriggerByRelayToScan; var ActiveFileName : String; myActiveFileCheckSum: String; begin // Enable the Print Monitor Button if(FLabJackConnected AND FRelayTriggeredScan AND (Not FTimerTriggeredScan)) then begin // After the first read.....The Monitorlatch is FALSE....it should only be true // again if BOTH voltages are less than 2.0V!!!! // Just get the voltage readings from the Labjack GetAnalogVoltage(0, FLJVoltage_0); GetAnalogVoltage(1, FLJVoltage_1); if ((FLJVoltage_0 < 2.0) and (FLJVoltage_1 < 2.0)) then FRelayMonitorLatch := True; // Update the Relay Lights Status if(FLJVoltage_0 > 2.0) then begin // Use 2V as the trigger voltage level for now //uiRelay_0_Light.Fill.Color := TAlphaColorRec.Lime; FLJRelay_0_ON := True; end else begin //uiRelay_0_Light.Fill.Color := TAlphaColorRec.Crimson; FLJRelay_0_ON := False; end; if(FLJVoltage_1 > 2.0) then begin // Use 2V as the trigger voltage level for now //uiRelay_1_Light.Fill.Color := TAlphaColorRec.Lime; FLJRelay_1_ON := True; end else begin //uiRelay_1_Light.Fill.Color := TAlphaColorRec.Crimson; FLJRelay_1_ON := False; end; // Now let's figure out if we need to take a photo // // The camera will be "inactive" most of the time // Make it active first.... // // then wait for Relay_1 to turn ON // // Take the photo and turn Relay 1 OFF and Turn Relay 0 OFF // if FLJRelay_0_ON then begin // This means the camera is "active" and updating images // // Now let's see if Relay 1 is ON if FLJRelay_1_ON then begin // Here you should execute scan! // if FRelayMonitorLatch then begin PythonScriptThread.StopPythonScript; // Now let's write the file! PythonScriptThread.RunPythonScript( editDevSerialNumber.Text, nioHorizontalRotation.Value, nioVerticalRotation.Value, nioZoomFactor.Value ); end; // Now set the MonitorLatch to False! FRelayMonitorLatch := False; end; end; end; end; end. Here is the python script: #!/usr/bin/env python import sys from pathlib import Path import open3d as o3d import numpy as np from genicam.genapi import NodeMap from harvesters.core import Component2DImage, Harvester from photoneo_genicam.components import enable_components, enabled_components from photoneo_genicam.default_gentl_producer import producer_path from photoneo_genicam.features import enable_software_trigger from photoneo_genicam.pointcloud import create_3d_vector, map_texture from photoneo_genicam.user_set import load_default_user_set from photoneo_genicam.visualizer import render_static def rotate_point_cloud(point_cloud, horizontal_angle_degrees, vertical_angle_degrees): # Convert angles from degrees to radians horizontal_angle_radians = np.deg2rad(horizontal_angle_degrees) vertical_angle_radians = np.deg2rad(vertical_angle_degrees) # Define the rotation matrix for horizontal rotation (around Y-axis) horizontal_rotation_matrix = np.array([ [np.cos(horizontal_angle_radians), 0, np.sin(horizontal_angle_radians)], [0, 1, 0], [-np.sin(horizontal_angle_radians), 0, np.cos(horizontal_angle_radians)] ]) # Define the rotation matrix for vertical rotation (around X-axis) vertical_rotation_matrix = np.array([ [1, 0, 0], [0, np.cos(vertical_angle_radians), -np.sin(vertical_angle_radians)], [0, np.sin(vertical_angle_radians), np.cos(vertical_angle_radians)] ]) # Apply the rotations to the point cloud point_cloud.rotate(horizontal_rotation_matrix, center=(0, 0, 0)) point_cloud.rotate(vertical_rotation_matrix, center=(0, 0, 0)) def render_with_zoom(point_cloud, zoom_factor): vis = o3d.visualization.Visualizer() vis.create_window() vis.add_geometry(point_cloud) # Get the view control and set the zoom level view_control = vis.get_view_control() view_control.set_zoom(zoom_factor) vis.run() vis.destroy_window() def main(device_sn: str, horizontal_angle: float, vertical_angle: float, zoom_factor: float): with Harvester() as h: h.add_file(str(producer_path), check_existence=True, check_validity=True) h.update() with h.create({"serial_number": device_sn}) as ia: features: NodeMap = ia.remote_device.node_map load_default_user_set(features) enable_software_trigger(features) features.Scan3dOutputMode.value = "CalibratedABC_Grid" enable_components(features, ["Intensity", "Range", "Normal"]) ia.start() features.TriggerSoftware.execute() with ia.fetch(timeout=10) as buffer: components = dict(zip(enabled_components(features), buffer.payload.components)) intensity_component: Component2DImage = components["Intensity"] point_cloud_raw: Component2DImage = components["Range"] normal_component: Component2DImage = components["Normal"] point_cloud = o3d.geometry.PointCloud() point_cloud.points = create_3d_vector(point_cloud_raw.data) point_cloud.normals = create_3d_vector(normal_component.data) point_cloud.colors = map_texture(intensity_component) # Rotate the point cloud as specified by the command-line arguments rotate_point_cloud(point_cloud, horizontal_angle, vertical_angle) # Render the point cloud with the specified zoom factor render_with_zoom(point_cloud, zoom_factor) if __name__ == "__main__": # Set default values #device_id = "FJE-061" #horizontal_angle = 360 #vertical_angle = 180 #zoom_factor = 0.1 # Override default values with command-line arguments if provided if len(sys.argv) > 1: device_id = sys.argv[1] if len(sys.argv) > 2: horizontal_angle = float(sys.argv[2]) if len(sys.argv) > 3: vertical_angle = float(sys.argv[3]) if len(sys.argv) > 4: zoom_factor = float(sys.argv[4]) # Run main function with values if len(sys.argv) > 4: main(device_id, horizontal_angle, vertical_angle, zoom_factor)
-
Yes. I purchased a license. great stuff! Thanks!
-
I am glad I found this thread the other day. I was able ot get a large 3MF file to load much faster using OXml instead using the DOM method. so I concure for OXml suggestion.
-
Hi, Have you tried this (saxforpascal) with Delphi 11.3? Or is there anything newer that may work. I am opening 3MF files and the XML files that are big take too long so I was wanting to convert to using SAX implementation of XML parsing. Thanks, David
-
I have abandoned the FMX option. Thanks.
-
I have done everything. The camera permissions is open. In VCL I am using other libraries that work just fine. Its the FMX app that doesn't work.
-
The example did not work, The Camera is in a state of "Stopped" after calling "StartCapture"
-
I have absolutely done all of that. I am not even getting any errors in the IDE.
-
Windows VCL application pauses when window is not focused...
DavidJr. replied to DavidJr.'s topic in VCL
thats exactly what I did. thanks.