Jump to content

iqrf

Members
  • Content Count

    43
  • Joined

  • Last visited

Everything posted by iqrf

  1. How to export from delphi to python type TColor = (RED = 5, GREEN = 8, BLUE = 9) and in the return value of the delphi function called from Python to return a PPyObject of type TColor. Is it any other way than clumsily like this? FEngine.PyRun_SimpleString('from enum import Enum' + #13 + 'Color = Enum(''Color'', {''RED'':5, ''GREEN'':8, ''BLUE'':9})'); ... FEngine.ExecString(UTF8Encode(FScript), UTF8Encode(FCode.Path)); ... //result my function mainModule, colorEnum, redColor: PPyObject; .... mainModule := PyImport_AddModule('__main__'); colorEnum := PyObject_GetAttrString(mainModule, 'Color'); redColor := PyObject_GetAttrString(colorEnum, 'RED'); result := redColor; ... if mymodule.test_func() == Color.RED: ... Thanks for the ideas.
  2. iqrf

    Using Delphi enum in Python

    Thank you, I got to that too var a := VarPythonCreate(pColor).value the second way var valueObj := PyObject_GetAttrString(pColor, 'value'); if Assigned(valueObj) then begin a := PyLong_AsLong(valueObj); Py_XDECREF(valueObj); end else PyErr_Print();
  3. iqrf

    Using Delphi enum in Python

    Hi, how to use PyArg_ParseTupleAndKeywords to get the value of a function parameter that is of type Python enum. E.g class color (enum): RED = 5 GREEN = 8 BLUE = 9 TestFunction (Color.RED) var pColor: PPyObject; if (PyArg_ParseTupleAndKeywords(args, kwargs, '|O', @keyPointerArray[0], @pColor) <> 0) then begin PyEnum_Check(pColor) returns False why? How to get value 5 from pColor? I tried this too var a := VarPythonCreate(pColor); VarIsEnum(a) also returns False in 'a' is the string 'color.RED' Thank you for your help.
  4. iqrf

    Using Delphi enum in Python

    OK 😞 So I define an enum in python and use it in delphi, which works. Now I understand why it is like this: procedure TButtonsRegistration.DefineVars(APyDelphiWrapper: TPyDelphiWrapper); begin inherited; APyDelphiWrapper.DefineVar('bkCustom', bkCustom); APyDelphiWrapper.DefineVar('bkOK', bkOK); APyDelphiWrapper.DefineVar('bkCancel', bkCancel); APyDelphiWrapper.DefineVar('bkHelp', bkHelp); APyDelphiWrapper.DefineVar('bkYes', bkYes); APyDelphiWrapper.DefineVar('bkNo', bkNo); APyDelphiWrapper.DefineVar('bkClose', bkClose); APyDelphiWrapper.DefineVar('bkAbort', bkAbort); APyDelphiWrapper.DefineVar('bkRetry', bkRetry); APyDelphiWrapper.DefineVar('bkIgnore', bkIgnore); APyDelphiWrapper.DefineVar('bkAll', bkAll); end; Thanks for your time.
  5. iqrf

    Using Delphi enum in Python

    Sure this is ok. I would like in Python a = TColor.RED print(type(a)) ### <enum 'TColor'> where TColor defined in Delphi
  6. iqrf

    Using Delphi enum in Python

    I don't want to define an enum in both Delphi and Python. I just want to use an enum defined in Delphi in Python. WrapDelphi I use both FPythonWrapper.DefineVar() and FPythonWrapper.Wrap(TDelphi, soOwned) and FPythonWrapper.RegisterDelphiWrapper(TPyClassWrapper<...>).Initialize But I have no idea how to use the same functionality for an enumeration type.
  7. iqrf

    Using Delphi enum in Python

    Thanks a lot. And how do I get the enumerated type TColor into Python?
  8. iqrf

    Using Delphi enum in Python

    I don't understand how I can write in Python a = TColor.RED without exporting to Python? var colorEnum := MainModule.Color; var Red := colorEnum.RED; result := Red; E2010 Incompatible types: 'PPyObject' and 'Variant' But that's the variant type, I need to return a PPyObject. Thanks
  9. Hi, i have this code TColumn = class(TPersistent) private FHeaderName: String; FVisible: Boolean; FWidth: Integer; FPosition: Integer; procedure SetHeaderName(const Value: String); procedure SetVisible(const Value: Boolean); procedure SetWidth(const Value: Integer); procedure SetPosition(const Value: Integer); public property HeaderName: String read FHeaderName write SetHeaderName; property Visible: Boolean read FVisible write SetVisible; property Width: Integer read FWidth write SetWidth; property Position: Integer read FPosition write SetPosition; end; TColumnWrapper = class(TPyClassWrapper<TColumn>) constructor CreateWith(APythonType: TPythonType; args, kwds: PPyObject); override; end; TTerminalLogSettings = class(TPersistent) private fColumn: TDictionary<String,TColumn>; function GetAutoScroll: Boolean; function GetSeparatorH: Boolean; function GetSeparatorV: Boolean; function GetViewData: Boolean; procedure SetAutoScroll(const Value: Boolean); procedure SetSeparatorH(const Value: Boolean); procedure SetSeparatorV(const Value: Boolean); procedure SetViewData(const Value: Boolean); public constructor Create; destructor Destroy; override; property AutoScroll: Boolean read GetAutoScroll write SetAutoScroll; property SeparatorH: Boolean read GetSeparatorH write SetSeparatorH; property SeparatorV: Boolean read GetSeparatorV write SetSeparatorV; property ViewData: Boolean read GetViewData write SetViewData; property Column: TDictionary<String, TColumn> read fColumn write fColumn; end; ... FPythonModule_TerminalLog := TPythonModule.Create(nil); FPythonModule_TerminalLog.Name := 'FPythonModule_TerminalLog'; FPythonModule_TerminalLog.Engine := FEngine; FPythonModule_TerminalLog.ModuleName := 'terminal_log'; FPythonModule_TerminalLog.OnInitialization := OnInitialization_TerminalLog; FPythonModule_TerminalLog.OnAfterInitialization := OnAfterInitialization_TerminalLog; ... FPythonVersion.AssignTo(FEngine); FEngine.LoadDll; ... terminalLogSettings := TTerminalLogSettings.Create; pTerminalLogSettings := FPythonWrapper.Wrap(terminalLogSettings, soOwned); FPythonModule_TerminalLog.SetVar( 'settings', pTerminalLogSettings); FEngine.Py_DecRef(pTerminalLogSettings); FPythonWrapper.RegisterDelphiWrapper(TColumnWrapper).Initialize; FPythonWrapper.RegisterDelphiWrapper(TPyClassWrapper<TDictionary<string, TColumn>>).Initialize; ... if used in this way in the script, then after the script finishes I correctly call the TTerminalLogSettings destructor and free the memory ... iqrfide.terminal_log.settings.autoScroll = True ... settings_object = iqrfide.terminal_log.settings setattr(settings_object, 'autoScroll', True) ... If the script is interrupted using exit(), the destructor is not called and the memory is not freed. Any explanation? ... iqrfide.terminal_log.settings.autoScroll = True ... settings_object = iqrfide.terminal_log.settings setattr(settings_object, 'autoScroll', True) exit() ... Putting settings_object = None before exit solves the problem.
  10. iqrf

    TPyDelphiWrapper - memory leaks

    Hi, not using exit() is not the solution for me. Users use exit() when debugging a script. If to ProcessSystemExit; i add Py_DecRef(errtraceback); so the memory leak problem will disappear. The destructor TTerminalLogSettings will already be called. I have no idea, but how is that possible. procedure TPythonEngine.CheckError(ACatchStopEx : Boolean = False); procedure ProcessSystemExit; var errtype, errvalue, errtraceback: PPyObject; SErrValue: string; begin PyErr_Fetch(errtype, errvalue, errtraceback); Traceback.Refresh(errtraceback); SErrValue := PyObjectAsString(errvalue); Py_DecRef(errtraceback); // workaround PyErr_Clear; raise EPySystemExit.CreateResFmt(@SPyExcSystemError, [SErrValue]); end;
  11. HI, I modified the example of Demo 32. Added TColumn and fColumn: TDictionary<String,TColumn>; property ColumnA: TDictionary<String,TColumn> read fColumn write fColumn; {$METHODINFO ON} TColumn = class(TPersistent) private FVisible: Boolean; FWidth: Integer; public property Visible: Boolean read FVisible write FVisible; property Width: Integer read FWidth write FWidth; end; TPoint = class(TPersistent) private fx, fy : Integer; fName : String; fColumn: TDictionary<String,TColumn>; public procedure OffsetBy( dx, dy : integer ); published property x : integer read fx write fx; property y : integer read fy write fy; property Name : string read fName write fName; property ColumnA: TDictionary<String,TColumn> read fColumn write fColumn; end; {$METHODINFO OFF} procedure TForm1.Button1Click(Sender: TObject); var DelphiPoint : TPoint; p : PPyObject; begin // Here's how you can create/read Python vars from Delphi with // Delphi/Python objects. DelphiPoint := TPoint.Create; DelphiPoint.x := 10; DelphiPoint.y := 20; DelphiPoint.ColumnA := TDictionary<String,TColumn>.Create; var value := TColumn.Create; value.Visible := True; value.Width := 455; DelphiPoint.ColumnA.Add('Note', value); // DelphiPoint will be owned and eventually destroyed by Python p := PyDelphiWrapper.Wrap(DelphiPoint, soOwned); PythonModule1.SetVar( 'myPoint', p ); // Note, that you must not free the delphi point yourself. // Instead use the GetPythonEngine.Py_XDECREF(obj) method, // because the object may be used by another Python object. PythonEngine1.Py_DecRef(p); // Excecute the script PythonEngine1.ExecStrings( memo1.Lines ); end; if i coll print(spam.myPoint.ColumnA.Count) print(spam.myPoint.ColumnA.ContainsKey("Note")) OK 1 True print(spam.myPoint.ColumnA["Note"]) #TypeError: 'Object' object is not subscriptable for key in spam.myPoint.ColumnA: #TypeError: 'Object' object is not iterable print(key) How to solve this please. How to make this work a = spam.myPoint.ColumnA.Column() a.Visible = True a.Width = 111 spam.myPoint.ColumnA.Add("Note1", a) Thanks
  12. It is possible to somehow distinguish whether the SetVisible function was called from new_column.visible = True or myPoint.ColumnA['Note1'].Visible = True procedure TColumn.SetVisible(const Value: Boolean); begin FVisible := Value; ... end; Thanks for the advice.
  13. Is there any way to iterate over TDictionary in Python? for key in spam.myPoint.ColumnA.Keys: #TypeError: 'Object' object is not iterable print(key) for key, value in spam.myPoint.ColumnA.Items: #TypeError: 'IndexedProperty' object is not iterable print(key, value)
  14. Many thanks once again. Question about TPyPoint. From python it is always called CreateWith, you cannot call Create. Why is that? I solved it like this. To write p = spam.Point() , I changed ii: to |ii PyArg_ParseTupleAndKeywords(args, kwds, '|ii:CreatePoint', @KeyPointerArray[0], @fx, @fy)
  15. Of course it works. I don't know how to put elements of type TColumn into a dictionary ColumnA in Python. This doesn't work col = spam.myPoint.Column() col.Visible = True col.Width = 255 spam.myPoint.ColumnA.["Note1"] = col or spam.myPoint.ColumnA.["Note1"] = spam.myPoint.Column(True, 255)
  16. Thank you very, very much. I struggled with it for two days. Please, how to make the TColumn class visible so it works col = spam.myPoint.TColumn() col.Visible = True col.Width = 255 spam.myPoint.ColumnA.["Note1"] = col or spam.myPoint.ColumnA.["Note1"] = spam.myPoint.TColumn(True, 255) or do I have to make TPyColumn = class(TPyDelphiPersistent)? Thanks
  17. It doesn't work, the class TPyClassWrapper is not defined anywhere
  18. iqrf

    Html Help: Ctrl+F not working

    Hi, I confirm that both CTRL+F and CTRL+C do not work under Delphi Alexandria + Win11 64b. Not working is: HtmlHelp(0, Application.HelpFile, HH_DISPLAY_TOC, 0); HtmlHelp(0, Application.HelpFile, HH_DISPLAY_INDEX, DWORD(PWideChar(''))); HtmlHelp(0, Application.HelpFile, HH_DISPLAY_SEARCH, DWORD(@Query)); Application.HelpContext() Calling HtmlHelpW instead of HtmlHelp works. A broken Application.HelpContext solved them like this: function TFormMain.ApplicationEvents1Help(Command: Word; Data: NativeInt; var CallHelp: Boolean): Boolean; begin HtmlHelpW(0, PWideChar(Application.HelpFile), HH_HELP_CONTEXT, Data); CallHelp := False; end; I used HH_HELP_CONTEXT because even when calling the Application.HelpContext the Command parameter contained 1 instead of 15. Of course, this solution does not work if HelpKeyword is used for components. A universal solution could be made by possibly recoding these constants. const { Commands to pass to WinHelp() } {$EXTERNALSYM HELP_CONTEXT} HELP_CONTEXT = 1; { Display topic in ulTopic } {$EXTERNALSYM HELP_QUIT} HELP_QUIT = 2; { Terminate help } {$EXTERNALSYM HELP_INDEX} HELP_INDEX = 3; { Display index } {$EXTERNALSYM HELP_CONTENTS} HELP_CONTENTS = 3; {$EXTERNALSYM HELP_HELPONHELP} HELP_HELPONHELP = 4; { Display help on using help } {$EXTERNALSYM HELP_SETINDEX} HELP_SETINDEX = 5; { Set current Index for multi index help } {$EXTERNALSYM HELP_SETCONTENTS} HELP_SETCONTENTS = 5; {$EXTERNALSYM HELP_CONTEXTPOPUP} HELP_CONTEXTPOPUP = 8; {$EXTERNALSYM HELP_FORCEFILE} HELP_FORCEFILE = 9; {$EXTERNALSYM HELP_KEY} HELP_KEY = 257; { Display topic for keyword in offabData } {$EXTERNALSYM HELP_COMMAND} HELP_COMMAND = 258; {$EXTERNALSYM HELP_PARTIALKEY} HELP_PARTIALKEY = 261; {$EXTERNALSYM HELP_MULTIKEY} HELP_MULTIKEY = 513; {$EXTERNALSYM HELP_SETWINPOS} HELP_SETWINPOS = 515; {$EXTERNALSYM HELP_CONTEXTMENU} HELP_CONTEXTMENU = 10; {$EXTERNALSYM HELP_FINDER} HELP_FINDER = 11; {$EXTERNALSYM HELP_WM_HELP} HELP_WM_HELP = 12; {$EXTERNALSYM HELP_SETPOPUP_POS} HELP_SETPOPUP_POS = 13; const {$EXTERNALSYM HH_DISPLAY_TOPIC } HH_DISPLAY_TOPIC = 0; {$EXTERNALSYM HH_HELP_FINDER } HH_HELP_FINDER = 0; {$EXTERNALSYM HH_DISPLAY_TOC } HH_DISPLAY_TOC = 1; {$EXTERNALSYM HH_DISPLAY_INDEX } HH_DISPLAY_INDEX = 2; {$EXTERNALSYM HH_DISPLAY_SEARCH } HH_DISPLAY_SEARCH = 3; {$EXTERNALSYM HH_SET_WIN_TYPE } HH_SET_WIN_TYPE = 4; {$EXTERNALSYM HH_GET_WIN_TYPE } HH_GET_WIN_TYPE = 5; {$EXTERNALSYM HH_GET_WIN_HANDLE } HH_GET_WIN_HANDLE = 6; {$EXTERNALSYM HH_ENUM_INFO_TYPE } HH_ENUM_INFO_TYPE = 7; {$EXTERNALSYM HH_SET_INFO_TYPE } HH_SET_INFO_TYPE = 8; {$EXTERNALSYM HH_SYNC } HH_SYNC = 9; {$EXTERNALSYM HH_KEYWORD_LOOKUP } HH_KEYWORD_LOOKUP = $d; {$EXTERNALSYM HH_DISPLAY_TEXT_POPUP } HH_DISPLAY_TEXT_POPUP = $e; {$EXTERNALSYM HH_HELP_CONTEXT } HH_HELP_CONTEXT = $f; {$EXTERNALSYM HH_TP_HELP_CONTEXTMENU } HH_TP_HELP_CONTEXTMENU = $10; {$EXTERNALSYM HH_TP_HELP_WM_HELP } HH_TP_HELP_WM_HELP = $11; {$EXTERNALSYM HH_CLOSE_ALL } HH_CLOSE_ALL = $12; {$EXTERNALSYM HH_ALINK_LOOKUP } HH_ALINK_LOOKUP = $13; {$EXTERNALSYM HH_GET_LAST_ERROR } HH_GET_LAST_ERROR = $14; {$EXTERNALSYM HH_ENUM_CATEGORY } HH_ENUM_CATEGORY = $15; {$EXTERNALSYM HH_ENUM_CATEGORY_IT } HH_ENUM_CATEGORY_IT = $16; {$EXTERNALSYM HH_RESET_IT_FILTER } HH_RESET_IT_FILTER = $17; {$EXTERNALSYM HH_SET_INCLUSIVE_FILTER } HH_SET_INCLUSIVE_FILTER = $18; {$EXTERNALSYM HH_SET_EXCLUSIVE_FILTER } HH_SET_EXCLUSIVE_FILTER = $19; {$EXTERNALSYM HH_INITIALIZE } HH_INITIALIZE = $1c; {$EXTERNALSYM HH_UNINITIALIZE } HH_UNINITIALIZE = $1d; {$EXTERNALSYM HH_SET_QUERYSERVICE } HH_SET_QUERYSERVICE = $1e; {$EXTERNALSYM HH_PRETRANSLATEMESSAGE } HH_PRETRANSLATEMESSAGE = $fd; {$EXTERNALSYM HH_GLOBALPROPERTY } HH_GLOBALPROPERTY = $fc;
  19. Hi, I have a TPythonType object TPyDevice = class(TPyObject) and Interface methods in it function DoSendAndReceive(args: PPyObject): PPyObject; cdecl; function TPyDevice.DoSendAndReceive(args: PPyObject): PPyObject; cdecl; var data: TBytes; pyBytes: PPyObject; timeout: Single; logging: Boolean; begin with GetPythonEngine do begin Adjust(@Self); logging := False; if PyArg_ParseTuple( args, 'S|fp:device.send_and_receive', @pyBytes, @timeout, @logging) <> 0 then begin data := PyBytesAsBytes(pyBytes); var response := False; .... .... if response then begin pyBytes := PyBytes_FromStringAndSize(PAnsiChar(data), Length(data)); Result := pyBytes; end else Result := ReturnNone; end else Result := nil; end; end; I use it like this response = device_manager.device.send_and_receive(dat, 0, True) or response = device_manager.device.send_and_receive(dat) I would like to call the function like this device_manager.device.send_and_receive(data=dat, timeout 0, logging=True) or device_manager.device.send_and_receive(data=dat, logging=True) I edited it like this function TPyDevice.DoSendAndReceive(args, kwds: PPyObject): PPyObject; cdecl; var data: TBytes; pyBytes: PPyObject; timeout: Single; logging: Boolean; keyArray: array of AnsiString; keyPointerArray: array of PAnsiChar; begin with GetPythonEngine do begin Adjust(@Self); logging := False; KeyArray := ['data', 'timeout', 'logging']; KeyPointerArray := [PAnsiChar(KeyArray[0]), PAnsiChar(KeyArray[1]), PAnsiChar(KeyArray[2])]; if PyArg_ParseTupleAndKeywords(args, kwds, 'S|fp:device.send_and_receive', @keyPointerArray[0], @pyBytes, @timeout, @logging) <> 0 then begin data := PyBytesAsBytes(pyBytes); var response := False; .... .... if response then begin pyBytes := PyBytes_FromStringAndSize(PAnsiChar(data), Length(data)); Result := pyBytes; end else Result := ReturnNone; end else Result := nil; end; end; TPyDevice = class(TPyObject) ... constructor CreateWith( PythonType : TPythonType; args : PPyObject ); override; // Basic services function Repr : PPyObject; override; function Str : PPyObject; override; function GetAttr(Key: PAnsiChar): PPyObject; override; function SetAttr(Key: PAnsiChar; Value: PPyObject): Integer; override; // Methods of TPyDevice ... // Class methods class procedure RegisterMethods(PythonType: TPythonType); override; class procedure RegisterMembers(PythonType: TPythonType); override; class procedure RegisterGetSets(PythonType: TPythonType); override; // Interface methods function DoSendAndReceive(args, kwds: PPyObject): PPyObject; cdecl; ... end; class procedure TPyDevice.RegisterMethods(PythonType: TPythonType); begin inherited; with PythonType do begin AddMethod('send_and_receive', @TPyDevice.DoSendAndReceive, 'device_manager.device.send_and_recive(data: bytes)'); .... end; end; FPythonType_Device := TPythonType.Create(nil); FPythonType_Device.Name := 'FPythonType_Device'; FPythonType_Device.GenerateCreateFunction := False; FPythonType_Device.Engine := FEngine; FPythonType_Device.OnInitialization := OnInitialization_PythonType_Device; FPythonType_Device.TypeName := 'Device'; FPythonType_Device.Prefix := ''; FPythonType_Device.Services.Basic := [bsGetAttr, bsSetAttr, bsRepr, bsStr]; FPythonType_Device.Module := FPythonModule_DeviceManager; pDevice := FPythonType_Device.CreateInstance; FEngine.CheckError; device := TPyDevice(PythonToDelphi(pDevice)); FPythonModule_DeviceManager.SetVar('device', pDevice); FEngine.Py_DecRef(pDevice); But I get TypeError: send_and_receive() takes no keyword arguments. Execution of the send_and_receive function will not start at all. I looked at the Demo32 example, but there it is used with CreateWith which has parameters args, kwds: PPyObject already PythoEngine.pas Thank you in advance for your help.
  20. I will answer myself. To register a method, you need to use AddMethodWithKeywords instead of AddMethod. It was enough to look at WrapDelphi.pas. I think it would be beneficial if Demo32 included an example of such a method. and still to fix KeyPointerArray := [PAnsiChar(KeyArray[0]), PAnsiChar(KeyArray[1]), PAnsiChar(KeyArray[2]), nil];
  21. Yes, according to Demo32 I implemented it. But it is an example of use in CreateWidth. I am trying to use it in a method.
  22. It's really not easy, so I'll accept the imperfection and try to solve it sometime in the future. Thanks
  23. Hi, Let's take Demo08 as an example where a myPoint object is created in Delphi. I need to detect on the Delphi side if the assignment spam.myPoint = None is made in Python. Cyclic polling using PythonModule1.GetVar('myPoint') is inappropriate for me. I would need something like OnChange in TPythonDelphiVar. I've studied PythonEngine.pas and can't think of anything. Thank you for your opinion.
  24. Couldn't this be done at the Python level using a decorator above the module? import types class MyModule(types.ModuleType): def __setattr__(self, name, value): print(f"Setting attribute {name} to value {value}") super().__setattr__(name, value) mymodule = MyModule('mymodule') mymodule.my_attribute = 42 print(mymodule.my_attribute)
  25. Hello, is it possible to somehow create a submodule. Something like this MainModule := TPythonModule.Create(nil); MainModule.ModuleName := 'spam'; SubModule := TPythonModule.Create(nil); SubModule.ModuleName := 'spam.test'; and call in Python import spam import spam.test spam.function1() spam.test.function2() it reports an error if i do it like this it's fine import spam import spam.test as T spam.function1() T.function2() Can it be solved somehow? Thanks for the ideas.
×