GLCanvas, ScrolledWindow, clipping problems

Here's a script to demonstrate the problem. The script creates a ScrolledWindow with two children. One is a panel which paints its own background. The other is a GLCanvas which clears its background to black. On my machine at least, the contents of the panel are properly clipped. The border of the GLCanvas is also properly clipped, but its contents are not.

To see what I mean, try running with the call to glFinish() commented out, and then run without the call commented out. Calling SwapBuffers on the canvas produces the same effect as glFinish().

Am I doing something dumb in initializing or rendering into the canvas? Thanks again for any help.

ยทยทยท

On Mar 7, 2005, at 5:47 PM, Mitch Chapman wrote:

Meanwhile, I'll try to build a simple demonstration of the problem. I suspect I can reproduce the problem by placing a single GLCanvas inside a single ScrolledWindow, at a small negative y offset.

--
Mitch

--cut here----cut here----cut here----cut here----cut here----cut here--
#!/usr/bin/env python

"""Demo a problem with clipping of glcanvases within scrolled windows."""

import wx
from wx import glcanvas
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *

class WhitePanel(wx.Panel):
     def __init__(self, *args, **kw):
         wx.Panel.__init__(self, *args, **kw)
         self.Bind(wx.EVT_PAINT, self._uiPaint)

     def _uiPaint(self, evt):
         dc = wx.PaintDC(self)
         dc.SetBrush(wx.WHITE_BRUSH)
         dc.SetPen(wx.RED_PEN)
         w, h = self.GetClientSize()
         inset = 0
         dc.DrawRectangle(inset, inset, w - inset * 2, h - inset * 2)

class Canvas(glcanvas.GLCanvas):
     def __init__(self, parent, **kw):
         kw.setdefault("attribList", [
             glcanvas.WX_GL_RGBA,
             glcanvas.WX_GL_DOUBLEBUFFER,
             glcanvas.WX_GL_STENCIL_SIZE, 8
             ])
         glcanvas.GLCanvas.__init__(self, parent, **kw)
         self._inited = False
         self.Bind(wx.EVT_ERASE_BACKGROUND, self._uiEraseBackground)
         self.Bind(wx.EVT_SIZE, self._uiSize)
         self.Bind(wx.EVT_PAINT, self._uiPaint)

     def _uiEraseBackground(self, evt):
         return

     def _uiSize(self, evt=None):
         w, h = self.GetClientSize()
         if evt is not None:
             w, h = evt.GetSize()

         if self.GetContext():
             self.SetCurrent()
             glViewport(0, 0, w, h)

         if evt:
             evt.Skip()

     def _uiPaint(self, evt):
         self.SetCurrent()
         self._initGL()
         self.draw()

     def _initGL(self):
         if not self._inited:
             self._setDimensions()

             glMatrixMode(GL_PROJECTION)
             glLoadIdentity()

             glMatrixMode(GL_MODELVIEW)

             glEnable(GL_DEPTH_TEST)
             glClearDepth(1.0)

             glClearColor(0, 0, 0, 0)

             self._inited = True

     def _setDimensions(self):
         # Work around a bug in GLCanvas geometry management by forcing
         # its dimensions the first time it's painted.
         w, h = self.GetClientSize()
         x, y = self.GetPosition()
         self.SetPosition((x + 1, y + 1))
         self.SetDimensions(x, y, w, h)
         self._uiSize()

     def draw(self):
         glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
         glFinish()

class Frame(wx.Frame):
     def __init__(self, parent):
         wx.Frame.__init__(self, parent, -1, "Demo GLCanvas Problem")

         self.panel = wx.ScrolledWindow(self, -1, size=(320, 240),
                                        style=wx.SUNKEN_BORDER)

         # Place a panel at a negative offset within the scrolled window,
         # to demonstrate that it is clipped properly.
         self.white = WhitePanel(self.panel, -1, pos=(-10, -10),
                                 size=(100, 100),
                                 style=wx.RAISED_BORDER)

         # Place a canvas. The border gets clipped, but as soon
         # graphics rendered via either glFinish or SwapBuffers are
         # not.
         self.canvas = Canvas(self.panel, pos=(110, -10),
                              size=(100, 100),
                              style=wx.RAISED_BORDER)

         self.sizer = wx.BoxSizer(wx.VERTICAL)
         self.sizer.Add(self.panel, 1, wx.EXPAND|wx.ALL, 20)

         self.SetSizerAndFit(self.sizer)

def main():
     app = wx.PySimpleApp()
     f = Frame(None)
     f.Show()
     app.MainLoop()

if __name__ == "__main__":
     main()