bugGNU Octave - Bugs: bug #60509, First initialization of graphics...


bug #60509: First initialization of graphics subsystem switches graphics_toolkit when octave-cli used

Submitted by:  None
Submitted on:  Mon 03 May 2021 02:12:15 PM UTC  
Category:  Plotting Severity:  3 - Normal
Priority:  5 - Normal Item Group:  Incorrect Result
Status:  Confirmed Assigned to:  None
Originator Name:  David Hülsmeier Originator Email:  -email is unavailable-
Open/Closed:  Open Release:  6.2.0
Operating System:  GNU/Linux

Add a New Comment (Rich Markup)

You are not logged in

Please log in, so followups can be emailed to you.


( Jump to the original submission)

Tue 04 May 2021 06:29:07 AM UTC, comment #11: 

Just to avoid introducing accidental regressions: IIUC, the code snippet from comment #7 was written to solve bug #41665 and was pushed here:

Markus Mützel <mmuetzel>
Project Member
Tue 04 May 2021 12:49:46 AM UTC, comment #10: 

In graphics.cc, the constructor for gh_manager is

gh_manager::gh_manager (octave::interpreter& interp)
  : m_interpreter (interp), m_handle_map (), m_handle_free_list (),
    m_next_handle (-1.0 - (rand () + 1.0) / (RAND_MAX + 2.0)),
    m_figure_list (), m_graphics_lock (),  m_event_queue (),
    m_callback_objects (), m_event_processing (0)
  m_handle_map[0] = graphics_object (new root_figure ());

  octave::gtk_manager& gtk_mgr = octave::__get_gtk_manager__ ("gh_manager");

  // Make sure the default graphics toolkit is registered.
  gtk_mgr.default_toolkit ();

Problem #1 is that default_toolkit is a read-only function defined in gtk-manager.h

    std::string default_toolkit (void) const { return dtk; }

It just returns a string which is discarded in the function above.

Is it a requirement that gh_manager exist before figures can be plotted?  If so then this would make a natural point to force at least one toolkit to be loaded, or error out.  But maybe we shouldn't even do this.  If a user is going to use gnuplot all of the time then there is no point loading the 'qt' or 'fltk' toolkits.

The toolkit needs to be stored on a per-figure basis, since it is switchable.  But the initialization needs to happen just once per toolkit.  Maybe we need to store 1 bit of state information in gtk_manager that says whether any toolkit has been initialized?  It might make even more sense to map this functionality on to a test for whether loaded_graphics_toolkits is empty or not.  If toolkits were unregistered this would naturally track the current status.

Rik <rik5>
Project Administrator
Mon 03 May 2021 11:15:27 PM UTC, comment #9: 

I remember this being a PITA which probably explains why the code became so convoluted.  Maybe stating the goals would help.  I think

1) If a toolkit has been selected by the programmer then use it.  STOP.

2) If no toolkit has been initialized then prefer an OpenGL toolkit (with "qt" higher priority to "fltk").  STOP.

3) If no OpenGL toolkit is available then choose something from the list of available_graphics_toolkits().  STOP.

4) If no toolkit is available, throw an error

We don't implement the first condition.

Rik <rik5>
Project Administrator
Mon 03 May 2021 11:00:29 PM UTC, comment #8: 

Who wrote this mess?  Probably me!  And probably there is a much simpler way for this to be managed.

Yes, I think we want the initial default the first of "qt", "fltk", or "gnuplot" that is available.  But that should just be initialized early on, before any call to graphics_toolkit can happen.  After that, anything set by graphics_toolkit should persist.

With octave-cli, "qt" will not be available, so then it is just "fltk" or "gnuplot".

Maybe we could do away with the idea of available_graphics_toolkits?  Do we need to maintain that list?  I forget why I thought it was needed.  What is it used for now? Could we just fail if the requested toolkit is not found?

John W. Eaton <jwe>
Project Administrator
Mon 03 May 2021 09:30:30 PM UTC, comment #7: 

I wish I knew the programmer's original intent.  One way to tackle this would be to change get_toolkit().  Another would be to change
register_toolkit ()

  gtk_manager::register_toolkit (const std::string& name)
    if (dtk.empty () || name == "qt"
        || (name == "fltk"
            && available_toolkits.find ("qt") == available_toolkits.end ()))
      dtk = name;

    available_toolkits.insert (name);

so that registering a toolkit also made it the default to the gtk_manager (not the default to Octave which would use whatever was set by graphics_toolkit().

Yet another choice would be to change how init_toolkit works.

Rik <rik5>
Project Administrator
Mon 03 May 2021 08:36:52 PM UTC, comment #6: 

get_toolkit() is definitely the source of the issue.  The function is located in gtk-manager.cc and I quote it in its entirety.

  gtk_manager::get_toolkit (void) const
    graphics_toolkit retval;

    if (dtk.empty ())
      error ("no graphics toolkits are available!");

    auto pl = loaded_toolkits.find (dtk);

    if (pl == loaded_toolkits.end ())
        auto pa = available_toolkits.find (dtk);

        if (pa == available_toolkits.end ())
          error ("default graphics toolkit '%s' is not available!",
                 dtk.c_str ());

        octave_value_list args;
        args(0) = dtk;
        feval ("graphics_toolkit", args);

        pl = loaded_toolkits.find (dtk);

        if (pl == loaded_toolkits.end ())
          error ("failed to load %s graphics toolkit", dtk.c_str ());

        retval = pl->second;
      retval = pl->second;

    return retval;

One can see that if the default toolkit is not loaded, but is available, it switches to the default toolkit by calling feval() to execute the m-file "graphics_toolkit".

I checked at the interpreter immediately after starting Octave and available_graphics_toolkits() returns { "fltk", "gnuplot" } while loaded_graphics_toolkits() is empty.  After executing "graphics_toolkit gnuplot" the new toolkit is listed by loaded_graphics_toolkits(). 

The reason why Octave seems to work when "--no-gui" is used is that loaded_graphics_toolkits() includes "qt" the moment Octave is started.  This means the true branch of the if statement is not taken and feval of "graphics_toolkit" is never called.  Just to prove that this is the correct diagnosis, I tried

run-octave --no-gui-libs -f
graphics_toolkit gnuplot
h = figure

This sequence works correctly because the call to _init_fltk_ creates the entry "fltk" in loaded_graphics_toolkits and then the true branch is not taken.

Rik <rik5>
Project Administrator
Mon 03 May 2021 08:20:04 PM UTC, comment #5: 

Unfortunately, there seems to be a race condition in the C++ code.

The m-file code in graphics_toolkit.m and figure.m looks correct to me.  Up until calling _go_figure_ in figure.m, the "DefaultFigure__graphics_toolkit__" property of the graphics root object (0) is properly set to "gnuplot".  After this call to C++, however, both the root object and the newly created figure have "fltk" as the graphics toolkit.

The initialization of a figure is fiendishly complex.  It seems that near the start all of the figure properties are initialized in graphics_props.cc.

graphics-props.cc:1327:  m["__graphics_toolkit__"] = default_graphics_toolkit ();

The default_graphics_toolkit function is defined as

static std::string
default_graphics_toolkit (void)
  octave::gtk_manager& gtk_mgr
    = octave::__get_gtk_manager__ ("default_graphics_toolkit");

  return gtk_mgr.default_toolkit ();

In stepping through the code, this returns "fltk" when "--no-gui-libs" option is used and "qt" when "--no-gui" is used.  Octave is clearly trying for an OpenGL toolkit.

The figure constructor in graphics-props.cc ends with a call to init() which is a protected function in graphics.h for figures.  This function ends with a call to init_toolkit ().  init_toolkit function is in graphics.cc and is simple.

figure::properties::init_toolkit (void)
  octave::gtk_manager& gtk_mgr
    = octave::__get_gtk_manager__ ("figure::properties::init_toolkit");

  toolkit = gtk_mgr.get_toolkit ();

Before this function, the figure private variable "toolkit" points to one location in memory.  After this, it points to another.  My guess is that this is where the switch is happening.

Rik <rik5>
Project Administrator
Mon 03 May 2021 03:34:13 PM UTC, comment #4: 

I can reproduce this on the development branch of Octave as well.  That's a good thing because it indicates something systemic that Octave is doing rather than related to a particular version of Octave.

Rik <rik5>
Project Administrator
Mon 03 May 2021 03:32:16 PM UTC, comment #3: 

Confirmed.  I get the same behavior when I explicitly start the interpreter using the octave-cli binary rather than the octave binary.

An even simpler test case is

graphics_toolkit gnuplot

The call to figure, and presumably the initialization of the graphics system for the first time switches the toolkit.

Rik <rik5>
Project Administrator
Mon 03 May 2021 03:10:12 PM UTC, comment #2: 

Hi and thank's for the fast reply.

this behavior just occurred when using octave-cli.
octave just links to octave-6.2.0 in /usr/bin, which also works for me.

I removed my ~/.octaverc file and ran the code again with your suggested flags (even though -f implies to not run ~/.octaverc when starting).
- octave-6.2.0, i.e., octave, the code works as intended, but with
- octave-cli-6.2.0 , i.e., octave-cli, I ran into the same odd behavior.


Mon 03 May 2021 02:45:16 PM UTC, comment #1: 

This works for me.  I tested the first stanza of code:

graphics_toolkit                       %--> gnuplot (expected)
t = linspace(0,1); y = sin(2*pi*t*10);
ans = gnuplot
ans = gnuplot

"gnuplot" is returned both times.  This is with Kubuntu 18.04.

You might try starting octave without initialization files in case there is something odd in either the system or personal configuration files.  I used

octave-6.2.0 -f --no-gui

Rik <rik5>
Project Administrator
Mon 03 May 2021 02:12:15 PM UTC, original submission:  


this error only occurs when using octave-cli, but not when using octave from the command line.

After opening a new figure with visible 'off', fltk is automatically selected as graphics toolkit.
This leads to a printing error.
After closing the  first graphic and setting the graphics toolkit to gnuplot, the printing process works.

Code example:

graphics_toolkit                       %--> gnuplot (expected)
t = linspace(0,1); y = sin(2*pi*t*10);
graphics_toolkit                       %--> fltk (bug; not expected)
print('-depsc2','-r300','bla.eps')     %--> error (see below)

close all
graphics_toolkit % --> gnuplot         %--> gnuplot (as desired)
print('-depsc2','-r300','bla.eps')     %--> ok

Error msg:

error: print: figure must be visible or qt toolkit must be used with __gl_window__ property 'on' or QT_OFFSCREEN feature available
error: called from
    __opengl_print__ at line 209 column 7
    print at line 757 column 16

Expected behavior:
The same graphics toolkit is used after the first call of "figures", and the figure is drawn with the 'visible','off' property.

Tested on
- Ubuntu 21.04; GNU Octave, version 6.1.1~hg.2021.01.26
- ArcoLinux; GNU Octave, version 6.2.0



(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:

No files currently attached


Depends on the following items: None found

Items that depend on this one: None found


Carbon-Copy List
  • -email is unavailable- added by mmuetzel (Posted a comment)
  • -email is unavailable- added by jwe (Posted a comment)
  • -email is unavailable- added by rik5 (Posted a comment)
  • -email is unavailable- added by None (Submitted the item)

    Do you think this task is very important?
    If so, you can add your encouragement to it.
    This task has 0 encouragements so far.

    Only project members can vote.




    Follow 4 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2021-05-03 rik5 StatusWorks For Me => Confirmed
        Summaryoctave-cli fails to print figure when 'visible' is 'off' => First initialization of graphics subsystem switches graphics_toolkit when octave-cli used
    2021-05-03 rik5 Item GroupNone => Incorrect Result
        StatusNone => Works For Me

    Back to the top

    Powered by Savane 3.6