QtHandles in a C++ application with embedded interpreter

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

QtHandles in a C++ application with embedded interpreter

Alex Leach
Hello,

I'm trying to build what I thought would be a fairly simple Qt Application, that calls a few .M script files. When running these scripts through the Octave GUI, figures are shown with the lovely QtHandles backend. However, I'm struggling to get figures to plot using the Qt Handles backend using my own app.

After realising that the Octave GUI is built with Qt, I thought it would be simple to write a Qt app that has an embedded interpreter. As my app is built with Qt in C++, the Qt core libraries are all linked in, I'm also linking to -loctave -loctinterp -loctgui. Is it possible then, to get figures to display using QtHandles in my app?

I have created a MyApplication class, that subclasses both QApplication and octave::application. This therefore has a pointer to an Octave interpreter, m_interpreter, with which I can run `m_interpreter->eval_string` or `octave::feval` to run code that's shipped with my app.

The last thing I've tried, is to run `m_interpreter->eval_string('__init_qt__', false, status)`, but this doesn't provide any output on the terminal and if I then list the available toolkits (I have a method printBackends, below), it only shows 'fltk' and 'gnuplot'. The qt backend is still not available. I've got a registerBackends method as well (see below), but this approach doesn't seem to work, causing seg-faults later on.

I've looked at the code in __init_qt__.cc, but without copying everything from libgui/graphics and building that with my app, I'm not sure how I can call into it? The headers aren't distributed with pre-built binaries, and I get linker errors if I try and call `F__init_qt__`, after defining it as per the DEFMETHOD_DLD(__init_qt__) macro:-

`extern octave_value_list F__init_qt__ (octave::interpreter& interp,
                                       const octave_value_list& args,
                                       int nargout);
`

Does anyone have any pointers how I can use QtHandles in my C++ app?

Thanks and kind regards,
Alex


void MyApplication::printBackends(void)
{
    Cell available_toolkits = m_interpreter->get_gtk_manager().available_toolkits_list();
    octave_value val;
    std::cerr << "Have " << available_toolkits.numel() << " toolkits available." << std::endl;
    for (octave_idx_type i = 0; i < available_toolkits.numel() ; i++ ) {
        val = available_toolkits(i);
        std::cerr << "Available Toolkit: "
                  << available_toolkits(i).string_value() << std::endl;
    }
}

void MyApplication::registerBackends(void)
{
    std::cerr << "Registering Qt Graphics" << std::endl;
    base_graphics_toolkit base_qttk = base_graphics_toolkit(std::string("qt"));
    graphics_toolkit qttk = graphics_toolkit(&base_qttk);

    m_interpreter->get_gtk_manager().load_toolkit(qttk);
    m_interpreter->get_gtk_manager().register_toolkit("qt");
    std::cerr << "Graphics registered" << std::endl;
}



Reply | Threaded
Open this post in threaded view
|

Re: QtHandles in a C++ application with embedded interpreter

siko1056
On 10/16/20 6:53 PM, Alex Leach wrote:

> Hello,
>
> I'm trying to build what I thought would be a fairly simple Qt
> Application, that calls a few .M script files. When running these
> scripts through the Octave GUI, figures are shown with the lovely
> QtHandles backend. However, I'm struggling to get figures to plot using
> the Qt Handles backend using my own app.
>
> After realising that the Octave GUI is built with Qt, I thought it would
> be simple to write a Qt app that has an embedded interpreter. As my app
> is built with Qt in C++, the Qt core libraries are all linked in, I'm
> also linking to -loctave -loctinterp -loctgui. Is it possible then, to
> get figures to display using QtHandles in my app?
>
> I have created a MyApplication class, that subclasses both QApplication
> and octave::application. This therefore has a pointer to an Octave
> interpreter, m_interpreter, with which I can run
> `m_interpreter->eval_string` or `octave::feval` to run code that's
> shipped with my app.
>
> The last thing I've tried, is to run
> `m_interpreter->eval_string('__init_qt__', false, status)`, but this
> doesn't provide any output on the terminal and if I then list the
> available toolkits (I have a method printBackends, below), it only shows
> 'fltk' and 'gnuplot'. The qt backend is still not available. I've got a
> registerBackends method as well (see below), but this approach doesn't
> seem to work, causing seg-faults later on.
>
> I've looked at the code in __init_qt__.cc, but without copying
> everything from libgui/graphics and building that with my app, I'm not
> sure how I can call into it? The headers aren't distributed with
> pre-built binaries, and I get linker errors if I try and call
> `F__init_qt__`, after defining it as per the DEFMETHOD_DLD(__init_qt__)
> macro:-
>
> [snip]
>



Hello Alex,

Your project sounds interesting, but there is the danger of reinventing
the wheel:

> write a Qt app that has an embedded interpreter.

That seems to be the exact definition of the GNU Octave GUI, where the
inter-thread communication between both players still gives some
developers headaches today.  Thus rather writing a "second GUI for
Octave", what is wrong with the "first" Octave GUI?

If I understand you right, you want to create advanced plots [1,2] with
widgets to tune/manipulate parameters, recompute portions, and get
feedback of the new computed values in a plot, right?  Putting the focus
here, to get things working the way you need them, is not sufficient for
your project?  Can you explain in more detail (code) what went wrong
with your first approach?

If you are still convinced your second approach is worth the effort, you
are the avant-garde on this field (embedding Octave for plotting,
embedding Octave for computing is way easier) and I am afraid you will
have to find out a lot by yourself, just like the original GUI
designers, who worked on it for years!  In this case, can you provide a
more complete example of your work?  From the snippets it is hard to get
a full picture of what you are doing and what might go wrong (best a
link to your repository with a short description how to run it).

Another interesting tutorial is embedded.cc [3], but there is a bug
about it, maybe you should wait for the resolution to integrate it to
your code [4].

HTH,
Kai

[1] https://octave.org/doc/v5.2.0/GUI-Development.html
[2] https://wiki.octave.org/Uicontrols
[3]
https://hg.savannah.gnu.org/hgweb/octave/file/0089f0213384/examples/code/embedded.cc
[4] https://savannah.gnu.org/bugs/?59228


Reply | Threaded
Open this post in threaded view
|

RE: QtHandles in a C++ application with embedded interpreter

Alex Leach
> -----Original Message-----
> From: Kai Torben Ohlhus <[hidden email]>
> Sent: 19 October 2020 06:51
> To: Alex Leach <[hidden email]>
> Cc: [hidden email]
> Subject: Re: QtHandles in a C++ application with embedded interpreter
>
> On 10/16/20 6:53 PM, Alex Leach wrote:
>
> Hello Alex,
>
> Your project sounds interesting, but there is the danger of reinventing the
> wheel:
>
> > write a Qt app that has an embedded interpreter.
>

Dear Kai,

Many thanks for the response. Haha, yes sometimes I fear I need to do the same, and that is exactly what I'm trying to avoid! The shared libraries and headers are there, though, I think, if I can call into them correctly.

> That seems to be the exact definition of the GNU Octave GUI, where the
> inter-thread communication between both players still gives some
> developers headaches today.  Thus rather writing a "second GUI for Octave",
> what is wrong with the "first" Octave GUI?
>
> If I understand you right, you want to create advanced plots [1,2] with
> widgets to tune/manipulate parameters, recompute portions, and get
> feedback of the new computed values in a plot, right?  Putting the focus
> here, to get things working the way you need them, is not sufficient for your
> project?  Can you explain in more detail (code) what went wrong with your
> first approach?

Essentially, what I'm trying to do, is port a MatLab application ('.mlapp') file to Octave. My colleagues, the end-users are not computer savvy at all, and have been shown how to use this mlapp only. The app has four tabs, in each of which a few parameters can be set, a file, folder or both can be selected and then when pressing the Run button, a .m function file is called with a  set of parameters.

The plots don't need to be re-computed after parameters are tuned or manipulated. Well, the functions can be re-run of course, but it will re-run the code from the beginning, as opposed to dynamically updating the figures etc.

When embarking on this approach, I did find the uicontrols page you provided, but to be honest I thought it would be more complicated doing it that way, I just wanted to redraw the mlapp in Qt Designer and then call the .m files, as per the embedded.cc example you showed. Seemed simple enough I thought!

> If you are still convinced your second approach is worth the effort, you are
> the avant-garde on this field (embedding Octave for plotting, embedding
> Octave for computing is way easier) and I am afraid you will have to find out a
> lot by yourself, just like the original GUI designers, who worked on it for
> years!  In this case, can you provide a more complete example of your work?
> From the snippets it is hard to get a full picture of what you are doing and
> what might go wrong (best a link to your repository with a short description
> how to run it).

Oh dear, that does sound like I've gone in the wrong direction for this project! Well, the embedded Octave I've got does show and launch figures okay, it's just that it shows them with the fltk backend, whereas the qt backend is much prettier(!), and perhaps easier to use, too.

I don't know what's going on with the '__init_qt__' function. It's not throwing an error and there's no output shown on stdout or stderr. Calling it from Octave-cli.exe shows 'error: __init_qt__: QApplication object must exist.', so I don't know exactly why in my app, it isn't registering the backend properly.

So, the GUI I've got does work actually quite well, it's just I'd prefer it used the Qt backend. I was hoping by staying within the realms of Qt, I might be able to reduce the number of DLLs and libraries required when redistributing this app with my colleagues.
 
> Another interesting tutorial is embedded.cc [3], but there is a bug about it,
> maybe you should wait for the resolution to integrate it to your code [4].

Oh, thanks, I hadn't seen that bug report. I was also getting seg-faults for a while, but I've found it very hard to track down seg-faults in Octave, as I've been unsuccessful compiling Octave from source (with debug symbols) on Msys for windows. A bit off topic that, however.

I will put together a minimal example and upload to my github account. I'll send a link shortly. I don't think it actually needs any widgets to demonstrate what I'm trying to do, just a sub-class of octave::application and octave::interpreter. The latter I did perhaps unnecessarily, so that I can also subclass QObject and pass it to a worker QThread, and then all .m files are called from the same thread. That's how I've gone about multi-threading, I haven't bothered with mutexes and locks, like octave_link provides. But it does mean that .m code is completely blocking and un-cancellable!

Thanks and Kind regards,
Alex

Reply | Threaded
Open this post in threaded view
|

RE: QtHandles in a C++ application with embedded interpreter

Alex Leach
> -----Original Message-----

> From: Alex Leach <[hidden email]>
> Sent: 19 October 2020 09:22
> To: Kai Torben Ohlhus <[hidden email]>
> Cc: [hidden email]
> Subject: RE: QtHandles in a C++ application with embedded interpreter
>
> I will put together a minimal example and upload to my github account. I'll
> send a link shortly. I don't think it actually needs any widgets to demonstrate
> what I'm trying to do, just a sub-class of octave::application and
> octave::interpreter. The latter I did perhaps unnecessarily, so that I can also
> subclass QObject and pass it to a worker QThread, and then all .m files are
> called from the same thread. That's how I've gone about multi-threading, I
> haven't bothered with mutexes and locks, like octave_link provides. But it
> does mean that .m code is completely blocking and un-cancellable!
>
Struggling to get into my github account, as have moved phone recently and need to reset my 2FA, which will take some time.

So, assuming attachments are acceptable, please find attached a relatively minimal project example created using Qt Creator.
This was tested on windows, so the include and lib build variables may need to be changed depending on your environment.

The concept is pretty simple here:-

1. Launch a QApplication with a QMainWindow containing a Start and Stop button.
  - On launch, MyInterpreter is created as well as a worker QThread.
2. When press Start:-
  - Try and register the qt graphics toolkit.
  - Move MyInterpreter to the QThread.
  - Send a signal from MyApplication to MyInterpreter.
  - MyInterpreter then calls a custom octave function that plots some graphs in a separate window.
3. Press Stop to close the MyApplication window and join all threads.

What I'm struggling with here is the MyInterpreter::registerGraphics method. I don't need to implement this, but I would like to, so it can register and use the Qt backend in my app, if possible. Do you know anyway to make this happen?

Thanks and kind regards,
Alex



TestOctaveQtHandles.zip (13K) Download Attachment