canvas paint event

I don’t understand what fires the paint event in a canvas control. I want to present pictures. Just before presenting them the name of the picture is determined dynamically so I want to use g.Drawpicture several times (with different pictures) using the canvas visible property in a countdown timer to do the precise timing. It seems that the paint event is fired when the window with the canvas is loaded. I use several canvases with several pictures to be able to independently time-schedule the visibility of these pictures.
But I would like to force the paint event to fire again.
Thanks

A paint event can fire at any time (for a number of reasons) or you can force it to happen when you want it to using canvas.refresh or canvas.invalidate

Refresh will happen right away while as Invalidate will happen when it gets a chance (might be 1ms or it might be 1s) depending on what is going on.

The simplest method is to use a timer and perform a canvas.refresh(false) when the timer fires (say every second).

With the timer you can decrement the number you mention (e.g. make it a window property) before calling the refresh.

Then on the paint, just draw the current number by referencing the window property mentioned above or use it to reference the picture you want to draw into the canvas.

Then you only need one canvas.

Here’s a little demo:

https://www.dropbox.com/s/yat8i7yngph9kqj/CavnasTimer.xojo_binary_project?dl=1

Note this method isn’t super accurate as things might get in the way of the refresh which might cause it to fire a few ms after, but for general use for a countdown timer it should be ok as long as this isn’t a scientific project :wink:

Thanks Julian,
OK this IS a scientific project but the exposure times of the pictures may be a few milliseconds off. There are different pictures at different locations that have to go on and off at different times. Originally I prepared such a sequence by loading the required pictures in originally invisible imagewells and run the sequence using the timer to make the Imagewells visible and invisible. But the imagewell has a border and I didn’t succeed to switch that off (it seems rectcontrol has borderwidth property but Imagewell did not inherit? Or do I totally misunderstand classes and inheritance). So I switched to canvas. Now it turns out that the visibility property of the canvas doesn’t affect the picture drawn in it. So the timing can’t be done using the visibility property of the canvas. Maybe I can return to the Imagewell approach and is there a way to remove the border of the imagewell?
Any suggestions are welcome.
Dick

If you download the project again I’ve updated it a bit to show you what you can do.

You could do this with one canvas and just draw the pictures where you need them to be or you can draw your pictures onto the background of the form and just refresh the areas that you’ve drawn so you don’t need to refresh the whole form.

There are many different ways to accomplish what you are trying to do, its just finding one that suits you and you know how to expect and take forward.

Dear Julian,

That looks like it. Of course exposures in my case may overlap but that can be solved using more timers.

I have one question for clarification. The action takes place in the timer through this control byname method (I don’t understand it but it works!). There the contents of the several canvases is refreshed. But it looks like the ‘refresh’ the second time is erasing. That is indeed what I need but I don’t understand it. Is the effect of refresh ‘alternating’.
Thanks for your help.
Dick

GetControlByName does exactly what its called, it gets the control by using its name as a string so we can reference a control using a name that is changing some how (by counter in this example)

So every time the timer fires (1000ms) it calls the canvas that is related to the counter.

Counter0.Refresh
Counter1.Refresh
etc

As the canvas are now of type MyCanvas (see Super in the inspector) the code will execute that is in MyCanvas.Paint when a Refresh is called which calls DoPaint (to keep things tidy).

As we are calling refresh twice and using the previousCounter and counter the code inside DoPaint will be doing different things on each refresh because we’re calling the previous canvas and the current canvas.

The first of the two refreshes are used to clear off the canvas related to the previousCounter, thus blank where the image last was, the code inside the IF wont run because Me.Name doesn’t equal “Canvas” + str(counter) because remember we came in here using the previousCounter canvas.

The second of the two refreshes are used to display the canvas related to the counter, thus draw where the image is now going to be. The code inside the IF will run because we called the refresh with

GetControlByName(Window1, "Canvas" + str(counter)).Refresh(False)

So when we compare Me.Name and “Canvas” + str(counter) they will be the same.

You could do the following in DoPaint:

If Me.Name = "Canvas" + str(counter) Then 'put your picture render code in here g.ForeColor = &c000000 g.DrawString("Frame " + str(counter), 20, 20) Else g.ForeColor = &cffffff g.FillRect(0, 0, g.Width, g.Height) End If

But it would need additional logic to clear down Canvas0 on initial startup, try it. This would however make for less overpainting as you would essentially not be clearing down an all ready cleared image.

You might want to do that though (clearing down an all ready cleared image) because you might be using semi-transparent images or images with transparent anti-aliasing, my example was pretty generic, things might need to change depending on what you’re doing etc.