Jump to content

Rolf Fankhauser

Members
  • Content Count

    22
  • Joined

  • Last visited

Posts posted by Rolf Fankhauser


  1. Hi,

     

    I generate in my application dynamically svg code using a javascript version of GraphViz (viz.js). The nodes are created as follows:

    <g class="node" id="node1"><title>B Regen</title>
    <polygon fill="none" stroke="black" points="63.9539,-396 0.0460627,-396 0.0460627,-360 63.9539,-360 63.9539,-396" />
    <text font-family="Times,serif" font-size="14" text-anchor="middle" x="32" y="-373.8">B Regen</text>
    </g>

    Because I want to show some data of the objects (nodes) on MouseOver I changed the innerText of <title> (which is shown as hint by default by the browser) by the following C++ code:

    // helpers for interface reference counting
      // could alternatively use TComInterface instead of DelphiInterface
      typedef DelphiInterface<IHTMLDocument3> _di_IHTMLDocument3;
      typedef DelphiInterface<IHTMLElement> _di_IHTMLElement;
      typedef DelphiInterface<IHTMLElementCollection> _di_IHTMLElementCollection;
      typedef DelphiInterface<IHTMLWindow2> _di_IHTMLWindow;
    
      // Get Data:
      _di_IHTMLDocument3 doc = CppWebBrowser1->Document;
      _di_IHTMLElement elem;
      _di_IHTMLElementCollection collection;
      _di_IHTMLWindow window;
      long len;
      TVariant value;
      // only for testing  
      OleCheck(doc->getElementById(WideString("svg"), &elem));
      if (elem) {
        elem->getAttribute(WideString("viewBox"), 2, &value);
        ShowMessage("viewBox: " + value);
      }
      
      // fill the title tags with data to show on Mouse over:  
      OleCheck(doc->getElementsByTagName(WideString("title"), &collection));
      collection->get_length(&len);
      //ShowMessage("Collection length: " + IntToStr(len));
      //ShowMessage(Commands->Text);  
      WideString tmp, tmp2;
      int line;
      for (long i = 0; i < len; ++i)
      {
        TVariant name = i;
        TVariant index = 0;
        DelphiInterface<IDispatch> disp;
        if(SUCCEEDED(collection->item(name, index, &disp)))
        {
          DelphiInterface<IHTMLElement> element;
          if( SUCCEEDED(disp->QueryInterface(IID_IHTMLElement, (LPVOID*)&element)))
          {
            element->get_innerText(&tmp); //reading element name
            line = FindElementInMemo(Commands, Trim(tmp)); //finding the element in a memo
            if (line != -1) {
              tmp2 = ExtractParameters(Commands->Strings[line]); //extracting the data to show
              //if (tmp != WideString("Output")) {
              if (tmp2 == WideString(""))
                tmp2 = WideString("a<br>b"); //test to format the data on two lines
              OleCheck(element->put_innerHTML(tmp2)); //replace the original text of the element name by the element data
            }
          }
        }
      }

    All is working but the text is not shown on multiple lines. ExtractParameters returns one line, so I made a test by creating constant data of 2 lines by "a<br>b". I tried with put_innerText and put_innerHTML but without success.

     

    Any idea why this doesn't work?

     

    Could I define a separate event handler for onmouseover using doc->execScript ?

     

    Thanks, Rolf


  2. In my application I generate a SVG graphic in a TCppWebBrowser by providing GraphViz dot language code that is converted to SVG by a JavaScript version of GraphViz viz.js.

    File _test.html shows the original HTML page (dot code and JavaScript code) that produces the SVG output shown in file test.html.

    In the form with the TCppWebBrowser component I added some buttons to zoom in, out and reset to 100%:

    //---------------------------------------------------------------------------
    void __fastcall TFormViewer::btZoomInClick(TObject *Sender)
    {
       ZoomFactor *= 1.1;
       OleVariant ZoomFac = int(ZoomFactor);
       CppWebBrowser1->ExecWB(63, OLECMDEXECOPT_DONTPROMPTUSER, ZoomFac, 0);
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TFormViewer::btZoomOutClick(TObject *Sender)
    {
       ZoomFactor *= 0.9;
       OleVariant ZoomFac = int(ZoomFactor);
       CppWebBrowser1->ExecWB(63, OLECMDEXECOPT_DONTPROMPTUSER, ZoomFac, 0);
    }
    //---------------------------------------------------------------------------
    //
    void __fastcall TFormViewer::btResizeClick(TObject *Sender)
    {
       ZoomFactor = 100;
       OleVariant ZoomFac = int(ZoomFactor);
       CppWebBrowser1->ExecWB(63, OLECMDEXECOPT_DONTPROMPTUSER, ZoomFac, 0);
    }

    This works fine but for huge networks with hundreds of elements I would like to zoom to a specific element by name (in test.html e.g. to "HE M15".  My idea is to change the viewBox attribute of the svg object. So, I need to find the element with the specific name (e.g. "HE M15"), read the x, y coordinates of the text and change the viewBox accordingly.

    Would this be possible by IHTMLWindow2::execScript (JavaScript code) or other tools to access the DOM...

    or would it be easier to save the html page, load it in a StringList, search the element and coordinates there and change the viewBox attribute, save the file and load it into the TCppWebBrowser component again?

     

    __test.html

    test.html


  3. @oliwe: I have already encountered VTK when I worked shortly with Paraview using some CFD software and VisIt (3D visualization of MRI slices) and recently when I was looking for a graph drawing library. Many programs and libraries are built on top of VTK.

    At the moment I use Graphviz in C++Builder for drawing graphs/networks. But I use a Javascript implementation of Graphviz viz.js. There is also a COM library WinGraphviz that could be used.


  4. I tried to install and use OpenCV under C++Builder 10.3.3 (see stack overflow), until now without success.

    Wouldn't it possible that Embarcadero could make this important library available via GetIt or at least provide a short installation tutorial with working dll's and import libs ?

    For Delphi there is a project on GitHub (Delphi-OpenCV) but it's an old version and I don't know if it can be used in C++Builder.

    At the moment there are 15 C++ libraries available on GetIt. Not very much compared to MS VC  (ca. 800) .

    I don't need 800 but it is rather frustrating if I see an interesting library but install/compiling instructions only for GCC or MS VC or just precompiled packages only for MS VC..

     

    Some libraries are easier to install than OpenCV, e.g. NLopt for non-linear optimization that I tested. Would also be a candidate for GetIt.

    Maybe I belong to a minority of RAD Studio users because I need many mathematical and scientific libraries and these libraries have low priority for Embarcadero. Then let me know...

    • Like 1

  5. I now installed the newest version with python 3.9.4. There is a file python39._pth

    (I checked it: Indeed, the python35 package didn't have a file python35._pth)

    So, I changed all according to the instructions.

    I installed numpy and matplotlib but when I try matplotlib from the command prompt and want to plot I get the following warning:

     UserWarning: Matplotlib is currently using agg, which is a non-GUI backend, so cannot show the figure.

    Only this backend "agg" is installed, no others.

    If I try to install Tkinter I get the same error as before with the python35 package: 

    ERROR: Could not find a version that satisfies the requirement Tkinter
    ERROR: No matching distribution found for Tkinter

    Can I use a Delphi window as backend?

    Saving the plot as png or svg and display it in Delphi would be a workaround but rather complicated.


  6. Thanks a lot !! This reference counting is annoying !! Thanks for the link, I will study the article.
    I supposed that ExtractPythonObjectFrom does increment the reference count.

    I corrected my code with your above suggestions. You forgot to remove PythonEngine1:

    var PyInput := PE.PythonEngine1.PyFloat_FromDouble(Input);

    Performance didn't change

     


  7. Ok, 1 mio iterations in 325 ms (average). That's close to the C-API version and 10 times slower than compiled. That is usable.

    That's the code:

    procedure TForm1.btRunScriptedClick(Sender: TObject);
    var
       i, i_end: integer;
       start, stop: TDateTime;
       start2, stop2: cardinal;
       sw : TStopWatch;
       script: TStringList;
       PyFunc, PyValue, PyArgs: PPyObject;
    
    begin
       sw := TStopWatch.Create;
       script := TStringList.Create;
       Memo2.Lines.Append('');
       Memo2.Lines.Append('loop started with python script, 1 mio iterations!');
       try
          i_end := 1000000;
          start := Now;
          start2 := GetTickCount;
          sw.Start;
          script.LoadFromFile('loop_function.py');
          PythonEngine1.ExecStrings(script);
          PyFunc := ExtractPythonObjectFrom(MainModule.calc_outflow);
          PyArgs := PythonEngine1.PyTuple_New(1);
    
          for i:=1 to i_end do
           begin
            Input := Random * 10.0;
            //PyValue := PythonEngine1.PyLong_FromLong(Input);  integer version
            PyValue := PythonEngine1.PyFloat_FromDouble(Input);
            PythonEngine1.PyTuple_SetItem(PyArgs, 0, PyValue);
            PyValue := PythonEngine1.PyObject_CallObject(PyFunc, PyArgs);
            //Output := PythonEngine1.PyLong_AsLong(PyValue); integer version
            Output := PythonEngine1.PyFloat_AsDouble(PyValue);
            //Memo2.Lines.Append('Input: ' + IntToStr(Input) + ', Output: ' + IntToStr(Output));
           end;
          stop := Now;
          stop2 := GetTickCount;
          sw.Stop;
          if sw.IsHighResolution = true then
           Memo2.Lines.Append('Is high resolution!')
          else
           Memo2.Lines.Append('Is not high resolution!');
          Memo2.Lines.Append('');
          Memo2.Lines.Append('Time by Now to run loop:           ' + FloatToStr(RoundTo((stop - start)*24*3600*1000, -3)) + ' ms');
          Memo2.Lines.Append('Time by GetTickCounts to run loop: ' + IntToStr(stop2 - start2) + ' ms');
          Memo2.Lines.Append('Time by StopWatch to run loop:     ' + IntToStr(sw.ElapsedMicroseconds) + ' us');
          Memo2.Lines.Append('Time by StopWatch to run loop:     ' + IntToStr(sw.ElapsedNanoseconds) + ' ns');
       finally
         { with PythonEngine1 do
          begin
           if Assigned(PyValue) then Py_DECREF(PyValue);
           if Assigned(PyArgs) then Py_DECREF(PyArgs);
           if Assigned(PyFunc) then Py_DECREF(PyFunc);
          end;  }
          sw.Free;
          script.Free;
       end;
    end;

    I have some problems with dereferencing the PPyObjects.

    Therefore I removed them. I got an AV when I run the loop multiple times with dereferencing. But I don't understand why.

    Without dereferencing I had no problems to run the loop multiple times. 

     


  8. I used the following code (not complete) to call the function in C++Builder:

     

    PyObject *pName, *pModule, *pDict, *pFunc;
    PyObject *pArgs, *pValue, *pType, *pTraceback;
    int i;
    ...
    pArgs = PyTuple_New(1);  
    ...  
    for (int j = 0; j < j_end; j++) {
       i = random(10);
       pValue = PyInt_FromLong(i);
       PyTuple_SetItem(pArgs, 0, pValue);
       pValue = PyObject_CallObject(pFunc, pArgs);
    }

    I guess that PPyObject (Delphi) is equivalent to PyObject (C)

    With ExtractPythonObjectFrom(MainModule.calc_outflow)  I would define pFunc, right?

    I will try it...


  9. Yes !! => now 1 mio iterations in 1800ms, not bad.

    I will try the low level functions of PythonEngine. It seems that they are wrapper of the C-API functions?

    Next step would be to use P4D in C++Builder because my application for sewer system simulation is written in C/C++

    I think there is a tutorial from David I for installation and use?

     

    Thanks for your prompt help!


  10. @pyscripter

    The compiler didn't accept the following line proposed by you:

     

    var CalcOutFlow: Variant := MainModule.calc_outflow;

    So, I split it:

    CalcOutFlow: Variant; (in var block)

    and

    CalcOutFlow := MainModule.calc_outflow;

     

    But then I get an error on line:    Output := CalcOutFlow(Random * 10.0);  (the compiler does not understand the bracket)

     


  11. Thanks for the hint!

     

    This was my next idea but I didn't yet know how to call the function.

    I already noticed this when I used the C API. Then I also changed from the script to a function and could improve the speed.

    For Lua I got an improvement of a factor 10 using a function.

     


  12. But the main program needs the value for each iteration, that's the real time control!

    The Delphi program transmits the input to the python script. The python script calculates the output according to the rule and gives it back to Delphi. This must happen in each iteration (time step).


  13.  

    I used the following script (from "deploying p4d.pdf") to list the existing modules:

     

    import sys
    import os
    print("Python path =", sys.path)
    print()
    print("Python modules already imported:")
    for m in sys.modules.values():
        if m:
            print(" ", m)
    print()
    print("PYTHONHOME =", os.getenv('PYTHONHOME'))
    print("PYTHONPATH =", os.getenv('PYTHONPATH'))

    Output:

    Python modules already imported:
      <module '_datetime' (built-in)>
      <module 'codecs' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\codecs.pyc'>
      <module 'builtins' (built-in)>
      <module 'encodings.aliases' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\encodings\\aliases.pyc'>
      <module '_frozen_importlib_external' (frozen)>
      <module 'io' (built-in)>
      <module 'time' (built-in)>
      <module 'encodings.mbcs' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\encodings\\mbcs.pyc'>
      <module 'genericpath' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\genericpath.pyc'>
      <module '_collections_abc' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\_collections_abc.pyc'>
      <module 'marshal' (built-in)>
      <module 'sys' (built-in)>
      <module '_weakrefset' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\_weakrefset.pyc'>
      <module 'zlib' (built-in)>
      <module 'os' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\os.pyc'>
      <module 'ntpath' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\ntpath.pyc'>
      <module 'stat' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\stat.pyc'>
      <module 'encodings' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\encodings\\__init__.pyc'>
      <module '_bootlocale' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\_bootlocale.pyc'>
      <module 'ntpath' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\ntpath.pyc'>
      <module '_weakref' (built-in)>
      <module 'encodings.latin_1' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\encodings\\latin_1.pyc'>
      <module 'abc' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\abc.pyc'>
      <module 'errno' (built-in)>
      <module 'site' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\site.pyc'>
      <module '_codecs' (built-in)>
      <module '_sitebuiltins' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\_sitebuiltins.pyc'>
      <module 'zipimport' (built-in)>
      <module '_thread' (built-in)>
      <module '_frozen_importlib' (frozen)>
      <module '_signal' (built-in)>
      <module '_locale' (built-in)>
      <module 'encodings.utf_8' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\encodings\\utf_8.pyc'>
      <module 'winreg' (built-in)>
      <module 'sysconfig' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\sysconfig.pyc'>
      <module 'math' (built-in)>
      <module 'pyio'>
      <module 'nt' (built-in)>
      <module '_stat' (built-in)>
      <module '_imp' (built-in)>
      <module '_warnings' (built-in)>
      <module 'datetime' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\datetime.pyc'>
      <module '__main__' (built-in)>
      <module 'io' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\io.pyc'>
      <module 'encodings.cp1252' from 'C:\\Data\\Delphi10-3\\TestPythonPerformance\\python\\python35.zip\\encodings\\cp1252.pyc'>
    
    PYTHONHOME = None
    PYTHONPATH = None

    It seems that only modules in python35.zip are imported


  14. Hi again,

     

    I would like to use P4D in my application for Real Time Control (RTC) in a program for simulation of sewer systems. The program needs to calculate 500'000 time steps or more. In each time step a Python script should set some controls according to some discharges in the system. The rules are system-specific and the users should be able to define the rules for their system. A perfect application for a scripting language!

    So, I created a small application to test the performance of P4D for RTC: a loop with 1 mio iterations.

    The loop in Delphi:

    for i:=1 to i_end do
           begin
            input := Random * 10.0;
            if (input > 6.0) or (input < 2.0)
            then
              output := 40.0
            else
              output := 20.0
           end;

    The Python script 'loop.py':

    if (inflow.Value > 6) or (inflow.Value < 2) :
      outflow.Value = 40
    else:
      outflow.Value = 20

     

    and the Delphi code to execute the script:

    	  script.LoadFromFile('loop.py');
          
          for i:=1 to i_end do
           begin
            PythonDelphiVar2.Value := Random * 10.0;
            PythonEngine1.ExecStrings(script);
           end;

     

    The results for Delphi (with different methods to measure the time):

     

    Time by Now to run loop:           28 ms
    Time by GetTickCounts to run loop: 15 ms
    Time by StopWatch to run loop:     27664 us
    Time by StopWatch to run loop:     27664600 ns

     

    and for P4D:

    loop started with python script, 1 mio iterations!

     

    Time by Now to run loop:           28830 ms
    Time by GetTickCounts to run loop: 28844 ms
    Time by StopWatch to run loop:     28828936 us
    Time by StopWatch to run loop:     28828936500 ns

     

    P4D is around 1000 times slower than Delphi! Some ideas to improve the performance would be very welcome.

     

    I made some tests with C++Builder running the Python script with the C API. Then Python is very fast: 200 ms for 1 mio iterations. But when I moved this test program from Win 7 to Win 10 it didn't work any more (no error message, silent close of the program when running the script)

    At the moment I use Lua:

       advantages: very fast (ca. 300 ms for 1 mio iterations), no use of external dll's, all is included in the main C++Builder application

       disadvantages: C API rather complicated, not so much modules but growing, no support for Delphi (maybe there is a component...) 

     

    Regards, Rolf

       

     

     


  15. Hi all

     

    I would like to deploy python with my Delphi application to my customers. So, I downloaded and installed a Windows embeddable package of Python in a subdirectory \python of my project folder.

    I set the following two lines in the OnCreate event handler of the main form:

    PythonEngine1.SetPythonHome('\python');
    PythonEngine1.LoadDLL;

    With this setting I can execute Python scripts but I cannot import numpy (ImportError: No module named 'numpy')

    I can import numpy from the command line, so it seems to be correctly installed:

    C:\Data\Delphi10-3\TestPythonPerformance\python>python
    Python 3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:38:48) [MSC v.1900 32 bit (Intel)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import numpy as np
    >>>

    I also tested if P4D uses the correct Python distribution with:

    import sys
    print (sys.version)

    and I get:    3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:38:48) [MSC v.1900 32 bit (Intel)]

     

    The structure of the Python installation is as follows (under \python):

     

    Scripts
    share
    Lib
        site-packages
            ...
            matplotlib
            ...
            numpy
            ...
            pandas
            ...

     

    Do I need to set a module path that P4D finds the modules?

     

    Another problem is that importing matplotlib (even on the command line) rises an error that tkinter is not installed. If I try to install tkinter I get an error: 

    ERROR: Could not find a version that satisfies the requirement tkinter (from versions: none)
    ERROR: No matching distribution found for tkinter

     

    Thanks, Rolf

     

     

×