iqrf 3 Posted February 28 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 Share this post Link to post
pyscripter 689 Posted February 28 You need to register the TDictionary<String,TColumn> in Form.Create, PyDelphiWrapper.RegisterDelphiWrapper(TPyClassWrapper<TDictionary<String,TColumn>>).Initialize; Have not tested, Does it work? Share this post Link to post
iqrf 3 Posted February 28 It doesn't work, the class TPyClassWrapper is not defined anywhere Share this post Link to post
pyscripter 689 Posted February 28 Please use the latest version from https://github.com/pyscripter/python4delphi/. Not the Getit version. Here PyDelphiWrapper.RegisterDelphiWrapper(TPyClassWrapper<TDictionary<string, TColumn>>).Initialize; works. You could also subclass TDictionary<string, TColumn>. type TColumnDict = class( TDictionary<string, TColumn>) end; and in Form.Create PyDelphiWrapper.RegisterDelphiWrapper(TPyClassWrapper<TColumnDict>).Initialize; 1 Share this post Link to post
iqrf 3 Posted February 28 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 Share this post Link to post
pyscripter 689 Posted February 28 (edited) Assuming your register the dictionary as shown above the following works. from spam import myPoint print(myPoint.ColumnA['Note'].Width) print(myPoint.ColumnA['Note'].Visible) Edited February 28 by pyscripter Share this post Link to post
iqrf 3 Posted February 28 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) Share this post Link to post
pyscripter 689 Posted February 28 (edited) To be able to create columns you need to also to customize the TColumn wrapper: TColumnWrapper = class(TPyClassWrapper<TColumn>) constructor CreateWith(APythonType: TPythonType; args, kwds: PPyObject); override; end; constructor TColumnWrapper.CreateWith(APythonType: TPythonType; args, kwds: PPyObject); var W: integer; P: PPyObject; begin Create(APythonType); GetPythonEngine.PyArg_ParseTuple(args, 'iO:CreateColumn', @W, @P); DelphiObject := TColumn.Create; DelphiObject.Width := W; DelphiObject.Visible := GetPythonEngine.PyObject_IsTrue(P) = 1; end; then register it in Form.Create PyDelphiWrapper.RegisterDelphiWrapper(TColumnWrapper).Initialize; PyDelphiWrapper.RegisterDelphiWrapper(TPyClassWrapper<TDictionary<string, TColumn>>).Initialize; Then the following code works: from spam import myPoint, Column print(myPoint.ColumnA['Note'].Width) print(myPoint.ColumnA['Note'].Visible) new_column = Column(100, False) myPoint.ColumnA.Add('Note1', new_column) print(myPoint.ColumnA['Note1'].Width) print(myPoint.ColumnA['Note1'].Visible) Edited February 28 by pyscripter 1 Share this post Link to post
iqrf 3 Posted February 28 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) Share this post Link to post
pyscripter 689 Posted February 28 (edited) 1 hour ago, iqrf said: 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) You could allow for both Point() and Point(x, y) by modifying CreateWith if PyArg_ParseTupleAndKeywords(args, kwds, '|ICreatePoint') then // no arguements else if PyArg_ParseTupleAndKeywords(args, kwds, 'ii|:CreatePoint', @fx, @fy) then // two integer arguements begin ... end; Edited February 28 by pyscripter Share this post Link to post
iqrf 3 Posted February 29 (edited) 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) Edited February 29 by iqrf Share this post Link to post
pyscripter 689 Posted February 29 (edited) 9 hours ago, iqrf said: for key in spam.myPoint.ColumnA.Keys: #TypeError: 'Object' object is not iterable print(key😞 Use for key in spam.myPoint.ColumnA.Keys.ToArray(): print(key) It is possible to allow for a more natural iteration, but it would require to hand craft the TDictionary wrapper. See for instance how it is done in TPyDelphiStrings in WrapDelphiClasses. Edited February 29 by pyscripter Share this post Link to post
iqrf 3 Posted March 6 On 2/28/2024 at 4:27 PM, pyscripter said: To be able to create columns you need to also to customize the TColumn wrapper: TColumnWrapper = class(TPyClassWrapper<TColumn>) constructor CreateWith(APythonType: TPythonType; args, kwds: PPyObject); override; end; constructor TColumnWrapper.CreateWith(APythonType: TPythonType; args, kwds: PPyObject); var W: integer; P: PPyObject; begin Create(APythonType); GetPythonEngine.PyArg_ParseTuple(args, 'iO:CreateColumn', @W, @P); DelphiObject := TColumn.Create; DelphiObject.Width := W; DelphiObject.Visible := GetPythonEngine.PyObject_IsTrue(P) = 1; end; then register it in Form.Create PyDelphiWrapper.RegisterDelphiWrapper(TColumnWrapper).Initialize; PyDelphiWrapper.RegisterDelphiWrapper(TPyClassWrapper<TDictionary<string, TColumn>>).Initialize; Then the following code works: from spam import myPoint, Column print(myPoint.ColumnA['Note'].Width) print(myPoint.ColumnA['Note'].Visible) new_column = Column(100, False) myPoint.ColumnA.Add('Note1', new_column) print(myPoint.ColumnA['Note1'].Width) print(myPoint.ColumnA['Note1'].Visible) 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. Share this post Link to post
pyscripter 689 Posted March 8 On 3/6/2024 at 6:08 PM, iqrf said: It is possible to somehow distinguish whether the SetVisible function was called from TColumn.SetVisible is called, so inside that method Self is the TColumn object on which the method is called. Share this post Link to post