Jump to content

shineworld

Members
  • Content Count

    343
  • Joined

  • Last visited

  • Days Won

    4

Posts posted by shineworld


  1. To fully integrate VTK I had to modify the DelphiVCL sources of Python4Delphi by adding a new VTKPanel class inherited from TCustomControl that exposes OnMouseDown, OnMouseMove, OnMouseUp, OnMouseWheel, OnResize, and OnPaint:

    {***
     *  TAKE CARE
     *  =========
     *  This units add extra components to integrate VTK.
     *
     **}
    
    {$I ..\Definition.Inc}
    
    unit WrapExtVTK;
    
    interface
    
    uses
      System.Classes,
      System.TypInfo,
      System.SysUtils,
      Winapi.Windows,
      Vcl.Controls,
      Vcl.Graphics,
      Vcl.StdCtrls,
    
      WrapDelphi,
      PythonEngine,
      WrapVclControls,
      WrapDelphiClasses;
    
    type
      TVTKPanel = class(TCustomControl)
      private
        FOnPaint: TNotifyEvent;
      protected
        procedure Paint; override;
      published
        property OnPaint: TNotifyEvent read FOnPaint write FOnPaint;
      published
        property OnMouseDown;       // TMouseEvent
        property OnMouseMove;       // TMouseMoveEvent
        property OnMouseUp;         // TMouseEvent
        property OnMouseWheel;      // TMouseWheelEvent
        property OnResize;          // TNotifyEvent
      end;
    
      TPyDelphiVTKPanel = class (TPyDelphiControl)
      private
        function  GetDelphiObject: TCustomControl;
        procedure SetDelphiObject(const Value: TCustomControl);
      protected
      public
        class function  DelphiObjectClass : TClass; override;
        // Properties
        property DelphiObject: TCustomControl read GetDelphiObject write SetDelphiObject;
      end;
    
    implementation
    
    { TVTKPanel }
    
    procedure TVTKPanel.Paint;
    begin
      inherited;
    
      if Assigned(FOnPaint) then
        FOnPaint(Self);
    end;
    
    { TPyDelphiVTKPanel }
    
    class function TPyDelphiVTKPanel.DelphiObjectClass: TClass;
    begin
      Result := TVTKPanel;
    end;
    
    function TPyDelphiVTKPanel.GetDelphiObject: TCustomControl;
    begin
      Result := TVTKPanel(inherited DelphiObject);
    end;
    
    procedure TPyDelphiVTKPanel.SetDelphiObject(const Value: TCustomControl);
    begin
      inherited DelphiObject := Value;
    end;
    
    { register the wrappers, the globals and the constants }
    
    type
      TThirdPartiesCtrlsRegistration = class(TRegisteredUnit)
      public
        function Name : string; override;
        procedure RegisterWrappers(APyDelphiWrapper : TPyDelphiWrapper); override;
        procedure DefineFunctions(APyDelphiWrapper : TPyDelphiWrapper); override;
        procedure DefineVars(APyDelphiWrapper : TPyDelphiWrapper); override;
      end;
    
    { TThirdPartiesCtrlsRegistration }
    
    procedure TThirdPartiesCtrlsRegistration.DefineFunctions(APyDelphiWrapper: TPyDelphiWrapper);
    begin
      inherited;
    end;
    
    procedure TThirdPartiesCtrlsRegistration.DefineVars(APyDelphiWrapper: TPyDelphiWrapper);
    begin
      inherited;
    end;
    
    function TThirdPartiesCtrlsRegistration.Name: string;
    begin
      Result := 'ThirdPartiesVTK';
    end;
    
    procedure TThirdPartiesCtrlsRegistration.RegisterWrappers(APyDelphiWrapper: TPyDelphiWrapper);
    begin
      inherited;
    
      APyDelphiWrapper.RegisterDelphiWrapper(TPyDelphiVTKPanel);
    end;
    
    initialization
      RegisteredUnits.Add(TThirdPartiesCtrlsRegistration.Create);
      System.Classes.RegisterClasses([TVTKPanel]);
    
    end.

    At this point now mouse management and window resizing works well,
    although during Resize there is a small instant where you see the white container window.

    from DelphiVCL import *
    
    from vtkmodules.vtkRenderingCore import vtkRenderWindow
    from vtkmodules.vtkRenderingUI import vtkGenericRenderWindowInteractor
    
    class vclVTKRenderWindowInteractor(VTKPanel):
        """
        A vclVTKRenderWindowInteractor for Python and DelphiVCL.
    
        Uses a vtkGenericRenderWindowInteractor to handle the interactions.
        Use GetRenderWindow() to get the vtkRenderWindow.
        """
    
        def __init__(self, owner):
            super().__init__(self, owner)
    
            # set default member values
            self._render_window = None
            self._render_interactor = None
            self._render_interactor_timer = None
    
            # assign control events
            self.OnMouseDown = self._on_mouse_down
            self.OnMouseMove = self._on_mouse_move
            self.OnMouseWheel = self._on_mouse_wheel
            self.OnMouseUp = self._on_mouse_up
            self.OnPaint = self._on_paint
            self.OnResize = self._on_resize
    
        def __getattr__(self, attr):
            """Makes the object behave like a vtkGenericRenderWindowInteractor"""
            if attr == '__vtk__':
                return lambda t=self._render_interactor: t
            elif hasattr(self._render_interactor, attr):
                return getattr(self._render_interactor, attr)
            else:
                raise AttributeError(self.__class__.__name__ + " has no attribute named " + attr)
    
        def _on_mouse_down(self, Sender, Button, Shift, X, Y):
            ctrl = 'ssCtrl' in Shift
            shift = 'ssShift' in Shift
            self._render_interactor.SetEventInformationFlipY(X, Y, ctrl, shift)
            if Button == 0:
                self._render_interactor.InvokeEvent("LeftButtonPressEvent")
            elif Button == 1:
                self._render_interactor.InvokeEvent("RightButtonPressEvent")
            elif Button == 2:
                self._render_interactor.InvokeEvent("MiddleButtonPressEvent")
    
        def _on_mouse_move(self, Sender, Shift, X, Y):
            ctrl = 'ssCtrl' in Shift
            shift = 'ssShift' in Shift
            self._render_interactor.SetEventInformationFlipY(X, Y, ctrl, shift, chr(0), 0, None)
            self._render_interactor.MouseMoveEvent()
    
        def _on_mouse_up(self, Sender, Button, Shift, X, Y):
            ctrl = 'ssCtrl' in Shift
            shift = 'ssShift' in Shift
            self._render_interactor.SetEventInformationFlipY(X, Y, ctrl, shift)
            if Button == 0:
                self._render_interactor.InvokeEvent("LeftButtonReleaseEvent")
            elif Button == 1:
                self._render_interactor.InvokeEvent("RightButtonReleaseEvent")
            elif Button == 2:
                self._render_interactor.InvokeEvent("MiddleButtonReleaseEvent")
    
        def _on_mouse_wheel(self, Sender, Shift, WheelDelta, MousePos, Handled):
            x = MousePos.X
            y = MousePos.Y
            ctrl = 'ssCtrl' in Shift
            shift = 'ssShift' in Shift
            self._render_interactor.SetEventInformationFlipY(x, y, ctrl, shift, chr(0), 0, None)
            if WheelDelta > 0:
              self._render_interactor.MouseWheelForwardEvent()
            else:
              self._render_interactor.MouseWheelBackwardEvent()
            Handled.Value = True
    
        def _on_resize(self, Sender):
            width = Sender.ClientWidth
            height = Sender.ClientHeight
            if self._render_window:
                self._render_window.SetSize(width, height)
                self._render_interactor.ConfigureEvent()
                self.Repaint()
    
        def _on_paint(self, Sender):
            pass
            if not self._render_interactor is None:
                self._render_interactor.Render()
    
        # == BEG: xxx
        #
    
        def GetRenderWindow(self):
            if self._render_window is None:
                # create render window and assign handle of this component
                hwnd = self.Handle
                self._render_window = vtkRenderWindow()
                self._render_window.SetWindowInfo(str(int(hwnd)))
    
                # create render interactor as generic render window interactor
                self._render_interactor = vtkGenericRenderWindowInteractor()
                self._render_interactor.SetRenderWindow(self._render_window)
    
                # add render interactor timer events to the observer
                self._render_interactor.AddObserver('CreateTimerEvent', self.CreateTimer)
                self._render_interactor.AddObserver('DestroyTimerEvent', self.DestroyTimer)
            return self._render_window
    
        def Render(self):
            self.update()
    
        def CreateTimer(self, obj, evt):
            if self._render_interactor_timer is None:
                self._render_interactor_timer = Timer(self)
                self._render_interactor_timer.Enabled = False
                self._render_interactor_timer.Interval = 10
                self._render_interactor_timer.OnTimer = self.TimerEvent
            self._render_interactor_timer.Enabled = True
    
        def DestroyTimer(self, obj, evt):
            self._render_interactor_timer.Enabled = False
            return 1
    
        def TimerEvent(self, Sender):
            if not self._render_interactor is None:
                self._render_interactor.TimerEvent()
    
        #
        # == END: xxx
    
    def vclVTKRenderWindowInteractorConeExample():
        """A simple example that uses the vclVTKRenderWindowInteractor class."""
    
        from vtkmodules.vtkFiltersSources import vtkConeSource
        from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer
        import vtkmodules.vtkRenderingOpenGL2
        import vtkmodules.vtkInteractionStyle
    
        class MainView(Form):
            def __init__(self, owner):
                super().__init__(owner)
                self.vtk_panel = vclVTKRenderWindowInteractor(self)
                self.vtk_panel.Parent = self
                self.vtk_panel.Align = 'alClient'
                self.vtk_panel.AlignWithMargins = True
    
                # set default control values
                self.DoubleBuffered = True
    
                # set main form events handlers
                self.OnClose = self.__on_form_close
    
            def __on_form_close(self, sender, action):
                if sender is None:
                    return
    
                if sender == self:
                    action.Value = caFree
    
        # initialize application
        Application.Initialize()
        Application.Title = ""
    
        # create and show view
        view = MainView(Application)
        try:
            def on_timer(Sender):
                coneActor.RotateX(5)
                coneActor.RotateY(2.5)
                coneActor.RotateY(1)
                widget.GetRenderWindow().Render()
                timer.interval = 100
    
            view.Show()
    
            widget = view.vtk_panel
    
            ren = vtkRenderer()
            widget.GetRenderWindow().AddRenderer(ren)
    
            cone = vtkConeSource()
            cone.SetResolution(8)
    
            coneMapper = vtkPolyDataMapper()
            coneMapper.SetInputConnection(cone.GetOutputPort())
    
            coneActor = vtkActor()
            coneActor.SetMapper(coneMapper)
    
            ren.AddActor(coneActor)
    
            # initialize and start render interactor
            widget.Initialize()
            widget.Start()
    
            timer = Timer(None)
            timer.Interval = 1000
            timer.OnTimer = on_timer
    
            FreeConsole()
            Application.Run()
        finally:
            view.Destroy()
    
    if __name__ == '__main__':
        vclVTKRenderWindowInteractorConeExample()

    Final result:


    Unfortunately VTKPanel will be available only with a custom DelphiVCL Python.
    Will be interesting to have a native control which does same behaviour aka ExternControl or similar name to manage Python objects can work with a windowed delphivcl control using window handle.


  2. Do not miss to add OnClose event in the main form and evaluate action for caFree

     

    class DesktopView(Form):
    
        def __init__(self, owner):
            # call vcl inherited view constructor
            super().__init__(owner)
    
            ...
    
            # set main form events handlers
            self.OnClose = self.__on_form_close
    
            ...
    
        def __on_form_close(self, sender, action):
            if sender is None:
                return
    
            if sender == self:
                action.Value = caFree
    
    	...
    
    def main():
        # initialize application
        Application.Initialize()
        Application.Title = ""
    
        # create and show main_view
        main_view = DesktopView(Application)
        try:
            main_view.Show()
            FreeConsole()
            Application.Run()
        finally:
            main_view.Destroy()
    
    # main entry point
    if __name__ == "__main__":
        main()

     


  3. A few small steps forward...

     

    Now VTK is displaying quite well but putrously I still haven't quite figured out if CustomControl is the right "hook" point for VTK.

    VTK requires docking to a control with Handle.

     

    But I assume Paint's native handling of the control should come disabled.

     

    Also, CustomControl (TCustomControl) lacks OnMouseXXX events to capture events to send to VTK's iterator.

    Perhaps the only solution is to add a control derived from TWindowControl to DelphiVCL that would accommodate what VTK requires for integration.


    In the video the comparison of the same code in DelphiVCL and PySide6 (QT), which I want to get rid of as soon as possible.

     

     


  4. The code is a real crap, I'm just trying the right way to get the thing working,
    but Python + DelphiVCL + VTK looks like it could go.
     

    from delphivcl import *
    
    from vtkmodules.vtkRenderingCore import vtkRenderWindow
    from vtkmodules.vtkRenderingUI import vtkGenericRenderWindowInteractor
    
    BASE_CLASS_NAME = WinControl
    
    class vclVTKRenderWindowInteractor(BASE_CLASS_NAME):
        def __init__(self, owner):
            BASE_CLASS_NAME.__init__(self, owner)
            self._RenderWindow = None
            self._Iren = None
    
        def __getattr__(self, attr):
            """Makes the object behave like a vtkGenericRenderWindowInteractor"""
            if attr == '__vtk__':
                return lambda t=self._Iren: t
            elif hasattr(self._Iren, attr):
                return getattr(self._Iren, attr)
            else:
                raise AttributeError(self.__class__.__name__ + " has no attribute named " + attr)
    
        def GetRenderWindow(self):
            if self._RenderWindow is None:
                hwnd = self.Handle
                self._RenderWindow = vtkRenderWindow()
                self._RenderWindow.SetWindowInfo(str(int(hwnd)))
    
                self._Iren = vtkGenericRenderWindowInteractor()
                self._Iren.SetRenderWindow(self._RenderWindow)
    
            return self._RenderWindow
    
        def Render(self):
            self.update()
    
    def vclVTKRenderWindowInteractorConeExample():
        """A simple example that uses the vclVTKRenderWindowInteractor class."""
    
        from vtkmodules.vtkFiltersSources import vtkConeSource
        from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer
        import vtkmodules.vtkRenderingOpenGL2
        import vtkmodules.vtkInteractionStyle
    
        class MainView(Form):
            def __init__(self, owner):
                self.vtk_panel = vclVTKRenderWindowInteractor(self)
                self.vtk_panel.Parent = self
                self.vtk_panel.Align = 'alClient'
                self.vtk_panel.AlignWithMargins = True
                self.vtk_panel.BevelInner = 'bvNone'
                self.vtk_panel.BevelKind = 'bkFlat'
                self.vtk_panel.BevelOuter = 'bvNone'
                self.vtk_panel.Color = clCream
                self.vtk_panel.ParentBackground = False
                self.vtk_panel.Caption = 'Hello World!'
    
                # set main form events handlers
                self.OnClose = self.__on_form_close
    
            def __on_form_close(self, sender, action):
                if sender is None:
                    return
    
                if sender == self:
                    action.Value = caFree
    
        # initialize application
        Application.Initialize()
        Application.Title = ""
    
        # create and show view
        view = MainView(Application)
        try:
            def on_timer(Sender):
                coneActor.RotateX(10)
                coneActor.RotateY(5)
                coneActor.RotateY(2.5)
                widget.GetRenderWindow().Render()
                timer.interval = 100
    
            view.Show()
    
            widget = view.vtk_panel
    
            ren = vtkRenderer()
            widget.GetRenderWindow().AddRenderer(ren)
    
            cone = vtkConeSource()
            cone.SetResolution(8)
    
            coneMapper = vtkPolyDataMapper()
            coneMapper.SetInputConnection(cone.GetOutputPort())
    
            coneActor = vtkActor()
            coneActor.SetMapper(coneMapper)
    
            ren.AddActor(coneActor)
    
            widget._Iren.Initialize()  # Initialize the interactor
            widget._Iren.Start()       # Start the interactor
    
            timer = Timer(None)
            timer.Interval = 1000
            timer.OnTimer = on_timer
    
            FreeConsole()
            Application.Run()
        finally:
            view.Destroy()
    
    if __name__ == '__main__':
        vclVTKRenderWindowInteractorConeExample()

     

     


     

    • Like 2

  5. Sorry but
     

    procedure TWorkOrderListFrame.WorkOrderFilesGridDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
    var
      R: TRect;
      C: TColor;
      Value: string;
      Canvas: TCanvas;
      PenColor: TColor;
      BrushColor: TColor;
      FileIndex: Integer;
      Data: TWorkOrderData;
      BackgroundColor: TColor;
      WorkOrdersSelectedRow: Integer;
    begin
      // avoids any change at header row
      if ARow = 0 then Exit;
    
      // checks and gets work order data
      WorkOrdersSelectedRow := WorkOrdersGrid.Selection.Top;
      if FWorkOrderManager.GetWorkOrderDataByIndex(WorkOrdersSelectedRow - 1, Data) then
    
      // evaluates file index
      FileIndex := ARow - 1;  // <--- Here FileIndex is initialized with ARow (in update cell grid ROW) - 1 because first row is fixed row.
      if (FileIndex < 0) or (FileIndex >= TWorkOrderData.FILE_DATA_SIZE) then Exit; // <--- here the FileIndex is checked to be inner 0 to TWorkOrderData.FILE_DATA_SIZE - 1) otherwise exit (none to do).
      if Data.FileData[FileIndex].FileName = '' then Exit;
    
      // recovers canvas
      Canvas := WorkOrderFilesGrid.Canvas;

    Sincerely FileIndex, at if (FileIndex < 0.... ) it already "defined" but compling phase say "undefined"...


  6. I feel exactly the same way about Hint and Warnings, usually I've got 0/0.
    They are an indispensable tool to guarantee good code, especially when we are talking about millions of lines in play.

    At the moment I am compiling this project still with Sydney, although I also have Athens on which I have yet to test many

    third-party libraries that require more up-to-date versions and generate too many Warnings.


  7. I have a strange warning in compilation with Sydney 10.4.1 that I just can't understand:

     

    procedure TWorkOrderListFrame.WorkOrderFilesGridDrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);
    var
      R: TRect;
      C: TColor;
      Value: string;
      Canvas: TCanvas;
      PenColor: TColor;
      BrushColor: TColor;
      FileIndex: Integer;
      Data: TWorkOrderData;
      BackgroundColor: TColor;
      WorkOrdersSelectedRow: Integer;
    begin
      // avoids any change at header row
      if ARow = 0 then Exit;
    
      // checks and gets work order data
      WorkOrdersSelectedRow := WorkOrdersGrid.Selection.Top;
      if FWorkOrderManager.GetWorkOrderDataByIndex(WorkOrdersSelectedRow - 1, Data) then
    
      // evaluates file index
      FileIndex := ARow - 1;
      if (FileIndex < 0) or (FileIndex >= TWorkOrderData.FILE_DATA_SIZE) then Exit;
      if Data.FileData[FileIndex].FileName = '' then Exit;
    
      // recovers canvas
      Canvas := WorkOrderFilesGrid.Canvas;

    [dcc64 Warning] osWorkOrderListFrame.pas(946): W1036 Variable 'FileIndex' might not have been initialized

    The line with the warning is:

    if (FileIndex < 0) or (FileIndex >= TWorkOrderData.FILE_DATA_SIZE) then Exit;

    but FileIndex is set just the line above....

    Any idea ?

    • Sad 1

  8. I alternate with a colleague of mine in development with Athens on the same PC.
    As soon as Athens 12.1 patch 1 came out, I installed it using the manual procedure.
    Yesterday my colleague continued working on the same PC, and not knowing that I had already installed the patch manually, he also applied it with Get It, as he saw it in the list of possible updates.

    Now I find myself with two ways of installing the same patch and I wonder if this may lead to problems in the proper functioning of the environment.

    I would like to avoid re-installing the whole package and the 20 or so third-party libraries we use from scratch.


    Any idea whether we have "broken" the toy ?

     


  9. I usually ask my questions about the Delphi code to ChatGPT 4.
    It is very fast and enougth precise.
    You can use same chat session to teach it when is wrong and it learn for next responses.

    Recently I've played with local AI using "ollama" and "mixtral" model for programming.

    Example in pastebin : https://pastebin.com/wWaLsw1V

    Mixtral model requires a lot of memory, I've 48GB in my PC, but I don't have a compatible CUDA GPU
    so will take 4 minutes to get the pastebin result using only I7 CPU.
     


  10. 38 minutes ago, Anders Melander said:

    I think you forgot the price of a Windows license...

     

    I know it's possible to run Windows on a Raspberry Pi, I just don't think it makes sense from a business or performance POW.

    We bought industrial Iot licence version for 45 to 89€ depending by number an type of cores. W10 1908 version. It works very fine. I in will prepare a video next week.

    • Like 2

  11. So to get the new 12.1 I've to uninstall and reinstall all by zero?
    1] I have got a lot of manually installed 3rd part libraries (13, ... uh...).
    2] What happens with the Installation counter? Do they take into account that I've uninstalled it?

    2nd question is important because I've two development PCs:
    1] main office desktop PC to do work when I'm in the office.
    2] A secondary portable notebook to do work when I'm at the customer's factory

    They are used mutually exclusive, or 1 or 2, but I've already used 2 installations shoots.

×