Hi, I am a very irregular user and recently updated my OS to Catalina and my years old Xojo to a new one because some apps didn’t work anymore. With the new Xojo I tried to build the old apps.
Apparently something had dramatically changed in the Canvas Graphics handling.
Whereas in the old paint event I could use:
Canvas1.graphics.pensize = x
This now results in a fatal error: Type Canvas has no member Graphics
and Type Int32 has no meter Pensize
and this is produced now basically for all code starting with Canvas1.Graphics.xxxxxx
Please help.I want my old apps to work again.
Use “Help => Language reference” and enter graphics into the search field. PenSize should work like:
g.PenSize = 5
g.DrawRectangle(150, 10, 100, 100)
you should get the graphics context as g
Sub Paint(g As Graphics, areas() As REALbasic.Rect) Handles Paint
g.PenSize = 5
Thanks for the replies,
I have the code to draw in the specific canvas, say Canvas1, at several locations in the program.For instance in window1 I have the canvas1 and somewhere in the ‘open’ code of window1 set the pensive to 4 by Canvas1,graphics,penszie =4. From another window it could be window1,Canvas1,Graphics pensize = 4. or window1,Canvas1.GRaphics.drawline xxxx for drawing a line.
Not all graphics properties are se and commands are givent inside the paint subroutine.
When inside the paint subroutine it seems that apparently automatically a g object is created. So within that event specific for a specific Canvas the pensize can be set to 4 by g.pensize = 4
How do I code graphics properties and commands for a specific canvas (and specific window) outside the canvas related paint?
To finish Markus’s thought:
You can no longer directly modify a canvas’ graphics context from outside the Paint event. All drawing must take place in the Paint event, or pass the Graphics context to a method from the Paint event.
You should probably be creating subclasses for specific elements you wish to draw, and handle the painting in their Paint events.
D… Most regrettably, it appears that this site’s “modesty filter” has corrupted your first name.
We found that the site has such a feature during the recent major “upgrade” to API 2 when some thought it appropriate to use a few “colorful” words to express their frustrations.
In the lingo of XOJO 2019 Release 2:
“!@#$%” is deprecated, you should use “Richard” instead.
It’s always had that feature, and it’s always been overzealous. It has nothing to do with API 2. The change in graphics drawing requirements also has nothing to do with API 2.
Create a property of the window called Canvas1_copy, of type picture
Whenever needed, (open? Activate? resize?), set Canvas1_copy to be the same size as the actual Canvas1
Canvas1_copy = new picture (canvas1.width, canvas1.height)
Swap all your Canvas1.graphics.something code so that it is
Then, in the Canvas1 Paint() event, do this:
I thought I had solved this one. When I open a window with a Canvas object it executes the paint event (*although we actually have an open (window)-event. It is stil not clear to me what that paint event is).
Anyway, I programmed a ‘select case’ switch INSIDE the paint event with the switch variable being global so that I could control from everywhere which parts of the paint event code are executed.
The goal is to have a real time graph which is updated in a timer call.<<
So in the timer-call which is executed with a fixed frequency I set the global variable that dictates which part of the paint is executed, update also some global drawline parameters and call Canvas.invalidate assuming that this would result in repeatng the execution of the paint event. But that doesn’t work. Invalidate does not re-execute the paint code (setting a break in the paint code did not trigger when the timer executed the canvas.invalidate. How would you implement a real time graph?
I would opt for subclassing Canvas to make it specific to every scenario you may have. Then, when the Timer fires it could put all the new values in the specific Canvas instance properties it should have in order to represent the new drawing. Finally, invoke “invalidate” to repainting the canvas instance.
Take into consideration that on macOS you can have a 1ms resolution between event fires for the Timer, but in Windows the lowest resolution you can reach is 16ms
but, you can lower that resolution to 1ms on Windows following the technique presented here or using MBS plugins, for example.
there is a nice example Graphics and Multimedia / GraphTest.
i tested it even with a timer that do the same as the changed slider event and it works fine. (2019r3)
Hi Markus, Ye I came across this example. But it lacks the realtime graphing. How would you modify graphTest so that the harmonic graph develops in time (as in a slow oscilloscope). THat is what I need. A timer object that updates the graph.
Just as you said, really.
Have a graphing subclass of Canvas with properties for your data. Do the actual drawing from those properties in the Paint event. Call Invalidate on the Canvas when you want to update it. That could be in a timer if you so desire.
i removed the ui frame “Graph Style” and replaced values with constants and then i put a random number in frequence.
i had add a timer into this window and set to 16 ms multiple. in action just
or instead of removing the whole frame graph style
just remove the Frequency Slider and add a property with type double.
Hi Markus, It is possible indeed to create an animation using a timer initiated 'invalidate’s. For instance you can have the sinus develop from x=0 by invalidate from the timer repeatedly and increasing the number of point to be drawn with each invalidate. This works but apparently the whole canvas is cleared with each time the invalidate retriggers the paint event. However this means that the whole graph is recalculated and redrawn, also the old points. The points that are graphed however in the actual app will be non calculable (measuring devices outputs). This means I either have to keep all the data in memory and redraw again and again the whole graph + new datapoint for each new measuring points.
Invalidate(false) is supposed to not clear the canvas but it does. (invalidate(true) does it also as it should do). It is actually no big deal to keep the measurements in memory but it should’nt be necessary, should it.
As you have noted, the demo file “GraphTest” works by generating a Sine wave plotted on the interval between the left and right edges of the canvas. The sliders, in effect, adjust the amplitude, “phase”, vertical offset, etc. but fundamentally this is a static xy line plot.
It would be helpful to know what you have in mind when you say “real time” graph. I imagine that you are trying to achieve something like this: see: http://media.gettyimages.com/photos/pulse-trace-monitor-closeup-picture-id200450671-001?s=170667a
I would consider this an “animation” problem where you are redrawing slightly different pictures in succession. To achieve the real time effect in this trace monitor, the “next” curve is similar to the current curve where the curve is shifted to the left and new points are added on the right.
In the case of a heart pulse trace monitor, I would store the xy data points for each curve in arrays. The arrays will have as many elements as needed to draw the curve across the screen at the desired resolution. Before the next time point is plotted after a time interval, the data in the arrays need to be updated. You will have to have some method that brings in the new data points before the next paint event.
I would look into using the Arrays.Pop and Arrays.AddRowAt methods to create a ‘stack’ where new data points are added at one end of the array and old data points are deleted from the other end (ie. the points that have crawled off the left of the screen, in the case of the trace monitor).
Hi Craig, thanks for your comment.
What I want is something like the pulse monitor but only for the time until the trace reaches the end of the screen. That should be possible by plotting each new point and not erasing everything and plot a whole array. Once the trace reaches the end of the screen and starts scrolling (horizontally for the pulse monitor) you need to redraw everything of course. In that case the solution with the array cannot be avoided.
I am a bit frustrated that the invalidate(false) call seems to erase the canvas which obliges me to work with an array that has to be redrawn with each new point . I think t shouldn’t. Invalidate(true) and invalidate(false) do the same.
i think you just need a scrolling picture, i will try to make a example
As Jeff said a while ago, add a picture as a property of the window. Draw onto the picture, pretend that picture is used the old way you used the canvas, then in the canvas.paint just call g.drawpicture(yourpicture,0,0) and splat your picture on the canvas. You can then update your picture whenever you want and just call invalidate on your canvas to draw it to the screen. All your historic drawn data is then retained on the picture to use later and you don’t need to store data to replot the graph over and over as its always kept on the picture until you modify it.