[GRASS-dev] Is wxNVIZ supposed to work?

Glynn Clements glynn at gclements.plus.com
Thu Nov 19 16:32:10 EST 2009

The wxGUI nviz module is broken for me in both 6.4.0-RC5 and 7.0-head.

In both cases, selecting "3D view" in the map display window results
in a segfault in the wxClientDC constructor.

The last few stack frames are:

#0  wxWindowDC (this=0xb225028, window=0x0)
    at /var/tmp/portage/x11-libs/wxGTK-
#1  0xb7558189 in wxClientDC (this=0xb225028, win=0x0)
    at /var/tmp/portage/x11-libs/wxGTK-
#2  0xb755834c in wxPaintDC (this=0xb225028, win=0xb11e420)
    at /var/tmp/portage/x11-libs/wxGTK-
#3  0xb6648a49 in _wrap_new_PaintDC (args=0x0, kwargs=0x0)
    at src/gtk/_gdi_wrap.cpp:24778
#4  0xb7fd7971 in PyCFunction_Call (func=0xa11f7cc, arg=0xae1074c, 
    kw=0xb11e420) at Objects/methodobject.c:85

Enabling debugging, the last message before it crashes is:

GUI D3/5: GLCanvas.OnPaint()

Combined, these point to it crashing here (nviz_mapdisp.py:156)

153    def OnPaint(self, event):
154        Debug.msg(3, "GLCanvas.OnPaint()")
156        dc = wx.PaintDC(self)
157        self.SetCurrent()
159        if not self.initView:
160            self.nvizClass.InitView()
161            self.initView = True

If I comment out that line (it doesn't need the DC, but apparently
this is needed on Windows to force the window contents to be marked as
"valid", otherwise it will just try to repaint it again), it crashes
within OpenGL:

#0  0xb41ed346 in glDisable () at ../../../src/mesa/x86/glapi_x86.S:368
#1  0xb42566a5 in gsd_set_clipplane (num=0, able=0) at gsd_prim.c:1028
#2  0xb424d5f0 in gsd_cplane_off (num=0) at gsd_cplane.c:129
#3  0xb423eeba in GS_unset_cplane (num=0) at GS2.c:3156
#4  0xb4938abc in Nviz_off_cplane (data=0xaef4188, id=0) at cplanes_obj.c:51
#5  0xb493a348 in Nviz_init_data (data=0xaef4188) at nviz.c:42
#6  0xb42c1c8d in Nviz::InitView ()
   from /usr/local/src/grass/svn/dist.i686-pc-linux-gnu/etc/wxpython/nviz/_grass7_wxnviz.so
#7  0xb42d82dc in _wrap_Nviz_InitView ()
   from /usr/local/src/grass/svn/dist.i686-pc-linux-gnu/etc/wxpython/nviz/_grass7_wxnviz.so
#8  0xb7eb29e9 in PyCFunction_Call (func=0xa8e784c, arg=0xabdd6ac, 
    kw=0xaef4188) at Objects/methodobject.c:116

My first instinct is that the widget isn't getting "realised" (i.e. 
"made real", meaning that the underlying X window has been created by
the X server).

But how is it getting a paint event if it hasn't been realised?

Ah: mapdisp.py:425 calls OnPaint immediately after creating the window:

419            if not self.MapWindow3D:
420                self.MapWindow3D = nviz.GLWindow(self, id=wx.ID_ANY,
421                                                 Map=self.Map, tree=self.tree, lmgr=self._layerManager)
422                # -> show after paint
423                self.nvizToolWin = nviz.NvizToolWindow(self, id=wx.ID_ANY,
424                                                       mapWindow=self.MapWindow3D)
425                self.MapWindow3D.OnPaint(None) # -> LoadData
426                self.MapWindow3D.Show()
427                self.MapWindow3D.UpdateView(None)

If I comment out line 425, it gets further before crashing in layout:

#0  0xb74fa1c3 in wxWindow::DoSetSize (this=0xa4fd178, x=1, y=81, width=768, 
    height=463, sizeFlags=4)
    at /var/tmp/portage/x11-libs/wxGTK-
#1  0xb75fa54f in wxSizerItem::SetDimension (this=0xa39e670, pos_=@0x0, 
    at /var/tmp/portage/x11-libs/wxGTK-
#2  0xb75fe29d in wxBoxSizer::RecalcSizes (this=0xa3b0980)
    at /var/tmp/portage/x11-libs/wxGTK-
#3  0xb75fb6a4 in wxSizer::Layout (this=0xa3b0980)
    at /var/tmp/portage/x11-libs/wxGTK-
#4  0xb75fa2b9 in wxSizer::SetDimension (this=0x1, x=0, y=0, width=0, height=0)
    at /var/tmp/portage/x11-libs/wxGTK-

At which point, I'm lost.

I think that this is an issue of trying to make calls before the
toolkit is ready for them. E.g. you can't create a widget then assume
that it's immediately ready for use; you have to wait for the toolkit
to report the appropriate event.

The comment at mapdisp.py:425:

425                self.MapWindow3D.OnPaint(None) # -> LoadData

suggests that it's relying upon the OnPaint method to load the data,
and the contents of the OnPaint method seem to support that.

But many of the things which are occuring there rely upon OpenGL
having been initialised. It's safe to assume that's the case if the
handler is called in response to an EVT_PAINT event, but it probably
won't be true if the program calls it itself as soon as the widget has
been created.

However, removing that call means that the nviz library and the binary
grass7_wxnviz module won't have been initialised, and UpdateView(),
OnSize() etc assume that they have. IOW, the code both needs to call
OnPaint() at that point but cannot call OnPaint() at that point.

The only solution is to check that initialisation has happened before
relying upon it. Being able to force initialisation as soon as the
widget is created would make life simpler, but unfortunately it isn't
possible; you have to wait until the widget is ready.

Glynn Clements <glynn at gclements.plus.com>

More information about the grass-dev mailing list