So, threads should NEVER talk to UI bits directly. Years ago I wrote a pretty complex subclass of Canvas that does a bunch of calculations and creates some pretty graphs. In retrospect, it would have been more flexible to have this all been in a subclass of Object and just write methods that do the drawing that anything can call. But - doing that refactoring now would be a MAJOR undertaking, and not something I want to mess with at the moment.
So, now I have the need to use this custom canvas subclass to create a bunch of these graphs in a batch from within a thread. I could go the Timers and Threads route, but would really prefer not to have to do that just to get at the functionality within this canvas.
So, I’d like to instantiate the canvas in code within the thread, use it to create my graphs, then destroy it when the thread is done with it.
This all seems to work fine, but I’ve run into one snag deep in the bowels of the canvas code where it needs access to its graphics, and it is always nil. I assume this is because it has not been embedded into anything yet… but is there any way I can get this canvas to have a non-nil graphics so I can use it?
I really do wish it was that simple, Dave. Unfortunately, in order to do the work, this canvas has several dozen methods and even more properties I’ve added to it that are used both for the generation of these graphs as well as lots of other things in the UI. So, the engineering time to refactor correctly as you suggest is much greater than just using timers to check on threads so I don’t violate having threads talk to the UI.
And when deadlines loom, shortest path to the finish line wins.
Was just hoping to be able to use this canvas directly from within a thread, by instantiating it in the body of the thread, using it, then destroying it without ever putting it into a window or other control.
How much of your subclass is based on Canvas? I mean if you’re instantiating it in code it seems you could switch to subclass Object (none) instead. Back it with a Picture and add a Graphics method returning the Graphics of that Picture and it should all be safe. Assuming there’s not too much else of Canvas you have to recreate.
Norm - I’ve not had issues before because everything in this class is happening as a result of a call to it’s paint event, where it draws onto its own graphics, which is perfectly sane and safe as long as you are not calling paint from within a thread. Paint there actually calls a bunch of other methods to do the drawing, which is what I want to do from a thread. I think that if I just use a picture there instead of its own graphics, I may be able to make things work.
@Markus Winter - Sort of. I’m creating a drawing that I’m caching to be shown later. The creation is somewhat expensive, so I want to pre-create in the background such that when it is time to show it the image is already available.
As I mentioned several times already in this thread, refactoring WILL take way too long. I’d love to be able to go back 8 years when I first started this project and undo some poor design decisions I made then that are finally really biting me badly, but at this point you’ll just have to trust me: This code is staying in a canvas subclass for the time being. I’ll either refactor a bit of where this canvas does its drawing (to a picture rather than to its internal graphics context) or I’ll split the work into both timers and threads so the threads never have to talk directly to the UI. Either of these solutions are several orders of magnitude less complex than refactoring this to be a picture rather than a canvas.
I’m wondering, if the canvas is never used as a UI element, couldn’t you just change the super to object, and add a picture property? How do you access the object? I’m guessing you’re using drawinto to get it to draw into a picture or window… you could add a “refresh” method that creates the picture object if needed and calls the paint event (now a method) with the picture’s graphics object. Add a “Drawinto” method with something like g.drawpicture(myPicture,0,0). Drawinto chould check for the image and call refresh if needed.
I obviously haven’t seen what you’re doing, but if you’re not using the UI aspects of Canvas, there isn’t a whole lot else there.
The canvas was originally designed for use as a ui element, and until now that is how it has bed used. It is still going to be used in the ui, I just want to automate what it does behind the scenes so that I can pre-create some stuff for a performance gain.
Maybe you could do what Jim suggested and change the super to object and add a picture property, then make a canvas subclass that wraps the class. Then you could use the new canvas subclass for the UI and the subclass of object for in the thread? Again, I don’t know how difficult this would be with your class but it might be able accomplish what you need.