FXRuby problem on Windows

Hi,
I’ve cut down a larger program to this test case that shows a problem on
windows. When I run this on my Linux box, I get an application window with
some colored boxes and the string "Hi There, (I’m on my way, I’m making
it) in it. When I run this same program on my Windows box, I get Zip,
nada, at first a gray window, then a transparent one if I minimize
thenmaximize the window. Some debugging statements show that onPaint is
getting called. Can anyone tell me what the problem is here? I know that
this sort of coding works because in my larger application, something
gets drawn to the screen on my windows box, certainly not everything that
is supposed to be drawn to it though, and on my Linux box, everything
works just peachy. The problem box is an older Compaq Armada laptop with
around 48 megs of memory running Windows 95, and the latest Ruby 1.6.8
installed from the sourceforge site that the Pragmatic Programmers so
kindly provide. Regular Widgets get drawn fine, but only the first part of
one canvas is ever drawn, and that is it, although the rest of the program
runs fine, I can tell because the other widgets (buttons, spinners,
labels, etc. ) are updated properly.

Here is the program:

#!/usr/bin/env ruby

require 'fox’
require 'fox/colors’
include Fox

class DcProb < FXMainWindow

def initialize(app)
super(app, “Show Drawing Context Problem”, nil, nil, DECOR_ALL, 0, 0,
400, 300) @canvas = FXCanvas.new(self, nil, 0 , (FRAME_SUNKEN|

FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT),0,0,400,3
00) @canvas.connect(SEL_PAINT, method(:onPaint))
@image = FXImage.new(app, nil, IMAGE_SHMI|IMAGE_SHMP)
@font= FXFont.new(app, “arial”, 12, FONTWEIGHT_BOLD)
end

def create
super

@font.create
@image.create

@canvasWidth = @canvas.getWidth
@canvasHeight = @canvas.getHeight

@image.resize( @canvasWidth, @canvasHeight )

@sdc = FXDCWindow.new( @canvas )
@idc = FXDCWindow.new( @image )


@idc.setForeground( FXColor::White )
@idc.fillRectangle( 0,0, @canvasWidth, @canvasHeight )

step = 5
0.step(50, step ) { |y|
  0.step(50, step ) { |x|
@idc.setForeground(FXRGB( rand(255), rand(255), rand(255)))
@idc.fillRectangle( x,y, x+@canvasHeight, y+@canvasWidth  )
  }
}

@idc.fillRectangle( 50,50, @canvasWidth, @canvasHeight  )
@idc.setForeground(FXColor::Black)
@idc.setTextFont( @font )
@idc.drawText( 60,80, "Hi There, (I'm on my way I'm making it!)")

show(PLACEMENT_SCREEN)

end

def onPaint( sender, sel, event )
@sdc.drawImage( @image, 0,0 )
end

end

if FILE == $0
application = FXApp.new(“DcProb”, “zepeda-zone.net”)
application.init(ARGV)
DcProb.new(application)
application.create
application.run
end

···


“Daniel P. Zepeda” <daniel@z,e,p,e,d,a,-,z,o,n,e.net>
(Remove commas for address)

When I run this same program on my Windows box, I get Zip,
nada, at first a gray window, then a transparent one if I minimize
thenmaximize the window.

I can confirm that same problem with the 1.7.3-7 version of the
Pragmatic Programmers installer. On a Win98 box with an ATI graphics card.

Its not really a transparent window though it simply pics up the context
of the dialog / area underneath, but if you move it it simply shows the
old context. You also get the same effect if you simply cover the box
and bring it to the front of the screen.

Rob

Daniel P. Zepeda wrote:

I’ve cut down a larger program to this test case that shows a problem on
windows. When I run this on my Linux box, I get an application window with
some colored boxes and the string "Hi There, (I’m on my way, I’m making
it) in it. When I run this same program on my Windows box, I get Zip,
nada, at first a gray window, then a transparent one if I minimize
thenmaximize the window. Some debugging statements show that onPaint is
getting called. Can anyone tell me what the problem is here?

I haven’t had a chance to look at this under Windows yet, but I think I
see what’s wrong; you need to call FXDC#end to “flush” out the drawing
commands that you’re making. The fact that it happens to work under
Linux is just a coincidence.

Device contexts (i.e. FXDCWindow instances) are just supposed to be
temporary objects, not things that you hang on to for the life of a
program. They are most often constructed and torn down within a
SEL_PAINT handler, although there are some other times you might need to
use them (for some examples, see the scribble.rb example program).

As an illustration (no pun intended), I’d recommend restructuring the
test case as follows. First, move the code for setting up the screen
device context (@sdc) into the SEL_PAINT handler for the canvas widget:

 def onPaint( sender, sel, event )
   sdc = FXDCWindow.new( @canvas )
   sdc.drawImage( @image, 0,0 )
   sdc.end
 end

Note that sdc doesn’t need to be (and probably shouldn’t be) an instance
variable, it’s just a local. Also note the call to FXDCWindow#end after
the drawing is complete.

Next, I’d move the code related to drawing the image out into a separate
routine:

 def setupImage
   @image.resize( @canvas.width, @canvas.height )

   idc = FXDCWindow.new( @image )

   idc.setForeground( FXColor::White )
   idc.fillRectangle( 0,0, @canvas.width, @canvas.height )

   step = 5
   0.step(50, step ) { |y|
     0.step(50, step ) { |x|
  idc.setForeground(FXRGB( rand(255), rand(255), rand(255)))
       idc.fillRectangle( x,y, x+@canvas.height, y+@canvas.width  )
     }
   }

   idc.fillRectangle( 50,50, @canvas.width, @canvas.height  )
   idc.setForeground(FXColor::Black)
   idc.setTextFont( @font )
   idc.drawText( 60,80, "Hi There, (I'm on my way I'm making it!)")

   idc.end
 end

As with onPaint(), the device context (idc) is now just a local
variable. I also replaced the instance variables @canvasWidth and
@canvasHeight with direct calls to the canvas for its width and height,
but that’s less important.

After these changes, the create() method is much simpler:

 def create
   super

   @font.create
   @image.create

   setupImage

   show(PLACEMENT_SCREEN)
 end

As I said, I haven’t tried it out on Windows yet, but I think the added
calls to FXDC#end will probably take care of the problem you were
seeing. If not, let me know and I’ll take a closer look.

Hope this helps,

Lyle

Daniel P. Zepeda wrote:

I've cut down a larger program to this test case that shows a
problem on windows. When I run this on my Linux box, I get an
application window with some colored boxes and the string "Hi
There, (I'm on my way, I'm making it) in it. When I run this same
program on my Windows box, I get Zip, nada, at first a gray
window, then a transparent one if I minimize thenmaximize the
window. Some debugging statements show that onPaint *is* getting
called. Can anyone tell me what the problem is here?

I haven’t had a chance to look at this under Windows yet, but I think I
see what’s wrong; you need to call FXDC#end to “flush” out the drawing
commands that you’re making. The fact that it happens to work under
Linux is just a coincidence.

You were absolutely correct. After changing the code as you suggested, it
works fine on both platforms. Thanks!

Device contexts (i.e. FXDCWindow instances) are just supposed to be
temporary objects, not things that you hang on to for the life of a
program. They are most often constructed and torn down within a
SEL_PAINT handler, although there are some other times you might need to
use them (for some examples, see the scribble.rb example program).

The documentation shows this as well. The reason I created the Device
Contexts and kept them around for the life of the application is that I
was using them to do crude animations. I figured (with no real basis) that
setting up/tearing down Device Contexts every 10 milliseconds or so was
wasteful and that just setting them up once and using them throughout the
application would make the program run better. Am I wrong? Given that I
want to use the Device Context every 10 milliseconds, doing crude
animation, do you still recommend that I set up/tear down the Device
Contexts each time, that is, for the example below, run the onPaint
example below every 10 ms with a slightly different @image each time?

Thanks for your stellar help Lyle.

Here is the refactored code for those that might benefit from it:

#!/usr/bin/env ruby

require ‘fox’
require ‘fox/colors’
include Fox

class DcProb < FXMainWindow

def initialize(app)
super(app, “Show Drawing Context Problem”, nil, nil, DECOR_ALL, 0, 0,
400, 300) @canvas = FXCanvas.new(self, nil, 0 , (FRAME_SUNKEN|

FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT),0,0,400,3
00) @canvas.connect(SEL_PAINT, method(:onPaint))
@image = FXImage.new(app, nil, IMAGE_SHMI|IMAGE_SHMP)
@font= FXFont.new(app, “arial”, 12, FONTWEIGHT_BOLD)
end

def create
super

@font.create
@image.create

setupImage

show(PLACEMENT_SCREEN)

end

def onPaint( sender, sel, event )
sdc = FXDCWindow.new( @canvas )
sdc.drawImage( @image, 0,0 )
sdc.end
end

def setupImage
canvasWidth = @canvas.getWidth
canvasHeight = @canvas.getHeight
@image.resize( canvasWidth, canvasHeight )

idc = FXDCWindow.new( @image )
step = 5
0.step(50, step ) { |y|
  0.step(50, step ) { |x|
idc.setForeground(FXRGB( rand(255), rand(255), rand(255)))
idc.fillRectangle( x,y, x + canvasHeight, y + canvasWidth  )
  }
}

idc.fillRectangle( 50,50, canvasWidth, canvasHeight  )
idc.setForeground(FXColor::Black)
idc.setTextFont( @font )
idc.drawText( 60,80, "Hi There, (I'm on my way I'm making it!)")
idc.end

end

end

if FILE == $0
application = FXApp.new(“DcProb”, “zepeda-zone.net”)
application.init(ARGV)
DcProb.new(application)
application.create
application.run
end

···

On Wed, 15 Jan 2003 02:14:55 +0900 Lyle Johnson lyle@users.sourceforge.net wrote:

Hope this helps,

Lyle


“Daniel P. Zepeda” <daniel@z,e,p,e,d,a,-,z,o,n,e.net>
(Remove commas for address)

Daniel P. Zepeda wrote:

Device contexts (i.e. FXDCWindow instances) are just supposed to be
temporary objects, not things that you hang on to for the life of a
program. They are most often constructed and torn down within a
SEL_PAINT handler, although there are some other times you might need to
use them (for some examples, see the scribble.rb example program).

The documentation shows this as well. The reason I created the Device
Contexts and kept them around for the life of the application is that I
was using them to do crude animations.

Ah.

I figured (with no real basis) that
setting up/tearing down Device Contexts every 10 milliseconds or so was
wasteful and that just setting them up once and using them throughout the
application would make the program run better. Am I wrong? Given that I
want to use the Device Context every 10 milliseconds, doing crude
animation, do you still recommend that I set up/tear down the Device
Contexts each time, that is, for the example below, run the onPaint
example below every 10 ms with a slightly different @image each time?

OK. In that case, you’re right that the cost of instantiating a new
FXDCWindow object every 10 milliseconds could start to bog things down.
So another approach that I think will work is to hang on to the device
contexts (as you did before) and then use FXDCWindow#begin to lock the
device context before drawing into it.

So I guess during initialization you’d want to construct the device
contexts (for the canvas and the image) and then immediately unlock them:

 @sdc = FXDCWindow( @canvas )
 @sdc.end
 @idc = FXDCWindow( @image )
 @idc.end

and then when you “really” need to use them, call begin and pass in the
associated drawable object:

 def onPaint( sender, sel, event )
   @sdc.begin( @canvas )
   @sdc.drawImage( @image, 0, 0 )
   @sdc.end
 end

Be sure to make a similar change in the image drawing routine(s).

Give this a shot, and if the performance still seems unacceptable, maybe
we can investigate some other optimizations (e.g. double-buffered
drawing of the image).

Hope this helps,

Lyle

So I guess during initialization you’d want to construct the device
contexts (for the canvas and the image) and then immediately unlock them:

@sdc = FXDCWindow( @canvas )
@sdc.end
@idc = FXDCWindow( @image )
@idc.end

and then when you “really” need to use them, call begin and pass in the
associated drawable object:

 def onPaint( sender, sel, event )
   @sdc.begin( @canvas )
   @sdc.drawImage( @image, 0, 0 )
   @sdc.end
 end

Be sure to make a similar change in the image drawing routine(s).

Give this a shot, and if the performance still seems unacceptable, maybe
we can investigate some other optimizations (e.g. double-buffered
drawing of the image).

Well, I’m glad that the app ran badly on windows, if you can believe that.
It forced me to re-evaluate my use of Device Contexts. My initial attempts
were clumsy at best. I have a better understanding now of Device Contexts
and their use. Thank you Lyle. The approach you suggested works well,
except that I ended up only using one DC and just associating it with each
drawable as needed. Since the code runs sequentially, drawing one thing
after another, it seemed a waste to use more than one DC. My application
runs just fine now under both Linux and Windows, and I’ve cut down on
unneeded resource usage even more.

Hope this helps,

Yes indeedy and thanks again for your time.

···

On Wed, 15 Jan 2003 05:15:39 +0900 Lyle Johnson lyle@users.sourceforge.net wrote:

Lyle


“Daniel P. Zepeda” <daniel@z,e,p,e,d,a,-,z,o,n,e.net>
(Remove commas for address)