Jump to content

shineworld

Members
  • Content Count

    131
  • Joined

  • Last visited

Community Reputation

26 Excellent

About shineworld

  • Birthday 05/09/1970

Technical Information

  • Delphi-Version
    Delphi 10.4 Sydney

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. shineworld

    Add #13#10 to a string

    Thanks for the replies. I've also missed to set WordWrap to True in button properties 🙂 Happens!
  2. shineworld

    Add #13#10 to a string

    It is embarrassing but I was not able to set a multiline text in a Button Caption. Usually in Delphi is only necessary to: MyButton.Caption = 'first line' + #13#10 + 'second line' What to get same behaviour in Python + DelphiVCL ?
  3. As like as David Heffernan I use the embedded python version for our customers. You can install pip in the embedded version so the end-user can add new packages or update packages from whl or Pypi. The embedded way is perfect to have an isolated python in which trust to deploy software for end-user. For example, my embedded version contains right OpenCV, NumPy, delphivcl/fmx, and support packages that I will use to create the end-user python program avoiding any conflicts with different versions already installed in other customer python installations and without use conda, etc.
  4. shineworld

    A question for thread reference

    Actually, I've used the simplest way to send an image to Python, send it completely (header, image structure, data). This required a python cv2.imdecode to get back a NumPy array clean of container (GIF/BMP/JPG/PNG/etc). You can use GetDIB in Delphi to extract only pure image data (RGB or RGBA), packet it, and return it to python then reorganize data in NumPy without the use of cv2.imdecode. TAKE CARE ======= In your time test for ByteIO, you have an overhead of cv2.imdecode.... Try without it.
  5. shineworld

    A question for thread reference

    Attached to the post there is a very simple Delphi application that should help you. The demo creates a PythonEngine and adds a new Module called delphi_vcl_ext in which wraps two functions: get_loaded_image_as_bytes() # get delphi loaded image as bytes update_image_from_bytes(...) # update delphivcl Image object from bytes array with width, height & channels The program has two panels: - Left Panel is a TImage and shows the loaded image to transfer to the Python script. - Right Panel is a TImage to show the python script evaluated image. By default, the program preloads a test BMP file (640x480 so fits the left image panel). Default script: - Get Delphi loaded image using get_loaded_image_as_byte(). - Decode the image to a NumPy array. - Apply an automatic canny filter. - Send back to Delphi the resulting image to be shown in the right panel. The time to transfer images is shown in the right log panel. With Load Image, you can try other files but must be supported by the TImage component. After all, is only a demo code made for you during rest time. Take care If you call script in a Delphi Thread you can't write python sent image directly in a TImage, or in any VCL component but use rightly Thread.Synchronized method to be done in the main thread. delphi_python_001.7z
  6. I've to move IMemento and IPersistable to JSON. I've already made that in Python and moving to Delphi should be simple. At moment I cannot do that, current project timings..... cnc_memento.py Delphi, Sydeny in my case, have a full JSON support. Here an example of element read to compare with python way.... Delphi JSON is very close to dict in python and so to related python json: function TAPITCPEngineClient.GetAxesInfo: TAPIAxesInfo; var Request: string; Response: string; JSONValue: TJSONValue; begin try if not GetActive then AbortFast; Result.Init; Request := '{"get":"axes.info"}'; Response := SendCommand(Request); if Response = '' then Exit; JSONValue := TJSONObject.ParseJSONValue(Response); try if not (JSONValue is TJSONObject) then Exit; if not JSONValue.TryGetValue<TAPIAxesArray>('["res"]["joint.position"]', Result.JointPosition.V) then AbortFast; if not JSONValue.TryGetValue<TAPIAxesArray>('["res"]["machine.position"]', Result.MachinePosition.V) then AbortFast; if not JSONValue.TryGetValue<TAPIAxesArray>('["res"]["program.position"]', Result.ProgramPosition.V) then AbortFast; if not JSONValue.TryGetValue<TAPIAxesArray>('["res"]["machine.target.position"]', Result.MachineTargetPosition.V) then AbortFast; if not JSONValue.TryGetValue<TAPIAxesArray>('["res"]["program.target.position"]', Result.ProgramTargetPosition.V) then AbortFast; if not JSONValue.TryGetValue<TAPIAxesSpeedArray>('["res"]["actual.velocity"]', Result.ActualVelocity.V) then AbortFast; if not JSONValue.TryGetValue<Cardinal>('["res"]["working.wcs"]', Result.WorkingWCS) then AbortFast; if not JSONValue.TryGetValue<TAPIAxesArray>('["res"]["working.offset"]', Result.WorkingOffset.V) then AbortFast; if not JSONValue.TryGetValue<Boolean>('["res"]["homing.done"]', Result.HomingDone) then AbortFast; if not JSONValue.TryGetValue<Cardinal>('["res"]["homing.done.mask"]', Result.HomingDoneMask) then AbortFast; Result.HasData := True; finally JSONValue.Free; end; except Result.Init; end; end; JSon is simplest than XML but more readable.
  7. shineworld

    XML DOM

    Usually, I use osMemento with BDS2006 (which is IDENTICAL to Delphi7) and Sydney. MSXML DOM is also very fast. With latest software, however, I'm migrating my settings files to JSON so I can open them in Python, where I've another implementation of Memento very close to Delphi version but for JSON.
  8. shineworld

    XML DOM

    CreateChild creates a new child empty node overwriting the eventual existent node. CreateChildSmart at first check if the child already exists and return it, maintaining contents otherwise it creates a new one. PS: I've missed a unit in git: osExceptionUtils.pas osExceptionsUtils add a new fast Abort (AbortFast) that I use often, but in source, you can replace any AbortFast with a native Abort.
  9. shineworld

    XML DOM

    If compile for Windows you can use MSXML directly to load/get/set/DOM and save. Here you can find how I use MSXML with Delphi implementing a light version of the Memento pattern: https://github.com/shineworld/memento Example of use in a system to load/save recently opened files: unit osMRUManager; interface uses osIMemento; type TMRUManager = class private FBackupPath: string; FCount: Integer; FItems: array of string; FMaxItems: Integer; private function GetItems(Index: Integer): string; procedure SetMaxItems(Value: Integer); public procedure Clear; procedure Delete(Index: Integer); function LoadFromFile(const FileName: string): Boolean; function LoadFromMemento(Memento: IMemento): Boolean; procedure Push(const Item: string); function SaveToFile(const FileName: string): Boolean; function SaveToMemento(Memento: IMemento): Boolean; procedure ValidateItems; public constructor Create; public property BackupPath: string read FBackupPath write FBackupPath; property Count: Integer read FCount; property Items[Index: Integer]: string read GetItems; property MaxItems: Integer read FMaxItems write SetMaxItems; end; implementation uses System.SysUtils, osIPersistable, osXMLMemento, osExceptionUtils; const DEF_MAX_ITEMS = 8; constructor TMRUManager.Create; begin // sets default members values FBackupPath := ''; FCount := 0; FItems := nil; FMaxItems := 0; // sets initial max items MaxItems := DEF_MAX_ITEMS; end; procedure TMRUManager.Delete(Index: Integer); var I: Integer; begin if (Index < 0) or (Index >= FMaxItems) then Exit; if (Index >= FCount) then Exit; for I := Index to FCount - 2 do FItems[I] := FItems[I + 1]; FItems[FCount - 1] := ''; Dec(FCount); end; procedure TMRUManager.Clear; begin FCount := 0; end; function TMRUManager.GetItems(Index: Integer): string; begin if (Index < 0) or (Index >= Count) then Result := '' else Result := FItems[Index]; end; function TMRUManager.LoadFromFile(const FileName: string): Boolean; var Memento: IMemento; BackupFileName: string; function GetBackupFileName: string; begin try if FBackupPath = '' then AbortFast; if not DirectoryExists(FBackupPath) then AbortFast; Result := FBackupPath + ExtractFileName(FileName); except Result := ''; end; end; function InternalLoadFromFile(const FileName: string): Boolean; begin try Memento := CreateReadRoot(FileName); if Memento = nil then AbortFast; if Memento.GetName <> 'mru_root' then AbortFast; if not LoadFromMemento(Memento) then AbortFast; Result := True; except Result := False; end; end; begin Clear; try BackupFileName := GetBackupFileName; Result := InternalLoadFromFile(FileName); if Result then begin if BackupFileName <> '' then SaveToFile(BackupFileName); Exit; end; Result := InternalLoadFromFile(BackupFileName); if not Result then AbortFast; SaveToFile(FileName); Result := True; except Clear; Result := False; end; end; function TMRUManager.LoadFromMemento(Memento: IMemento): Boolean; var I: Integer; W: TXMLString; MainNode: IMemento; FileNodes: IMementoArray; begin Clear; try MainNode := Memento.GetChild('mru'); if MainNode = nil then AbortFast; FileNodes := MainNode.GetChildren('file'); if FileNodes = nil then AbortFast; if Length(FileNodes) > MaxItems then AbortFast; FCount := Length(FileNodes); for I := 0 to FCount - 1 do begin if not FileNodes[I].GetString('name', W) then AbortFast; FItems[I] := W; end; Result := True; except Clear; Result := False; end; end; procedure TMRUManager.Push(const Item: string); var I: Integer; J: Integer; begin for I := 0 to FCount - 1 do begin if FItems[I] = Item then begin if I = 0 then Exit; for J := I downto 1 do FItems[J] := FItems[J - 1]; FItems[0] := Item; Exit; end; end; if FCount < FMaxItems then Inc(FCount); for I := FCount - 1 downto 1 do FItems[I] := FItems[I - 1]; FItems[0] := Item; end; function TMRUManager.SaveToFile(const FileName: string): Boolean; var Memento: IMemento; BackupFileName: string; function GetBackupFileName: string; begin try if FBackupPath = '' then AbortFast; if not DirectoryExists(FBackupPath) then AbortFast; Result := FBackupPath + ExtractFileName(FileName); except Result := ''; end; end; function InternalSaveToFile(const FileName: string): Boolean; begin try Memento := CreateWriteRoot('mru_root'); if not SaveToMemento(Memento) then AbortFast; if not (Memento as IPersistable).SaveToFile(FileName, nrmd_UTF8, False) then AbortFast; Result := True; except Result := False; end; end; begin try BackupFileName := GetBackupFileName; if not InternalSaveToFile(FileName) then AbortFast; if BackupFileName <> '' then InternalSaveToFile(BackupFileName); Result := True; except Result := False; end; end; function TMRUManager.SaveToMemento(Memento: IMemento): Boolean; var I: Integer; Node: IMemento; MainNode: IMemento; begin try MainNode := Memento.CreateChildSmart('mru'); for I := 0 to Count - 1 do begin Node := MainNode.CreateChild('file'); Node.PutString('name', FItems[I]); end; Result := True; except Result := False; end; end; procedure TMRUManager.SetMaxItems(Value: Integer); var I: Integer; begin if FMaxItems = Value then Exit; SetLength(FItems, Value); for I := FMaxItems to Value - 1 do FItems[I] := ''; FMaxItems := Value; if FCount > FMaxItems then FCount := FMaxItems; end; procedure TMRUManager.ValidateItems; var I: Integer; begin I := FCount - 1; while I >= 0 do begin if not FileExists(FItems[I]) then Delete(I); Dec(I); end; end; end.
  10. Could be interesting, to have time to write something about.... Natively the Delphi project is around 10 million lines and with the introduction of expandability via Python I don't know where it will end 🙂 Fortunately, in an I7, Delphi is so fast that it takes just over 3 minutes to compile the whole project. In another language, I could go for a quick lunch.
  11. I work in a company that makes control boards for CNC and related control environments on PC, but I don't physically own a CNC 🙂 However, we also drive 5-axis CNCs and more. The PC control part has always been done with Delphi and I couldn't be happier. I have abandoned other development environments and find that as fast and productive as Delphi there is no other, at least for this sector. I work in software/firmware development for embedded boards and proprietary real-time OS but in recent years most of my time is spent with my nose in Delphi pascal 🙂 I'm starting to get old to still work with assembler and C...
  12. However, in my case, Python + OpenCV + DelphiVCL is very very very fast in a big range of cases. Previously UI was TkInter, followed by PsSimpleGUI but very limited in UI things.
  13. Very interesting the vision lab project.
  14. To communicate between Delphi pure UI process and Python process I've implemented an API Server, based on TCP/IP + JSON encrypted messages. A Python package (cnc-api-client-core in PyPi) implements the same protocol (API Client). CNC Board covers any type of CNC (plasma, laser, mill, etc, till 9-Axis in EtherCAT Drivers or 6-Axis Step/DIR Drivers). With Python API Client interface I can do anything on CNC Control Software and CNC Board, exposing CNC features to any Python UI interface. Here an old example of Python process which use API Client and PySimpleGUI UI to interact with CNC: Now UI was moved to DelphiFMX to be used in Windows and Linux OS.
  15. A moment I've tried to create a simple framework package for Python language. The goal is to add vision features such as finding two printer markes to rotate and scale a CAD/CAM toolpath before running in a CNC. UI main program is a pure Delphi program. The called external program is a Python + delphivcl + opencv + skia and more other packages.
×