Recent

Author Topic: Painting on a control - 3 questions: OnPaint, ImageList, program logic  (Read 3356 times)

majolika

  • New Member
  • *
  • Posts: 41
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #30 on: January 23, 2025, 10:42:47 am »
Use a Timer and refresh the paintbox every 33ms (1000/30) to get something like 30fps.
Is like real games works.
Thanks for advice! Somewhen I'll use it!

majolika

  • New Member
  • *
  • Posts: 41
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #31 on: January 23, 2025, 10:56:33 am »
If the onpaint method is removed from the object inspector and set the dimensons of the paintbox at form create event and after that assign the onpaint event...
.. and have the buttonclick event read:
Then the paintbox onpaint event will not be called at that click and as a result the grid will not be painted and will stay empty.
That's definitly clear!

Hence we have to trigger the event ourselves by either using .refresh, .update, .invalidate or invoke the paint method (whatever is applicable for the situation).
Googling and reading this topic gives me understanding that
refresh — refreshes the component internally without repainting it;
update — refresh + repaint;
invalidate — tells the component that its visuals is outdated and then repaint only visible part of component (maybe some part off screen or cover by another window, whatever)
// there is some unclearness: are refresh and update not telling the component that its visuals is outdated? What's the point?
repaint — just repaint
// and there is some unclearness too: what's the difference between calling Repaint and calling OnPaint method? For example, PaintBox1.Repaint or PaintBox1.OnPaintDoSomeStuff.

See also attached project.
Thanks for you time! I've read it and here some things...

Quote
  // actually we should calc the row and column of the cell that contains a bomb
  // and then draw the complete grid as above, making an exception for the cell
  // that contains the bomb and draw the bomb instead of a 'normal' closed cell.
No, we shouldn't. If there are some other already opened cells drawing the complete grid will override it.
In my real program I'm doing exactly how you mentioned beneath — working with the dynamic array of cells and repainting (or not, if it's already opened) only one that clicked.
Actually I was a fool and doing all my drawings in the onclick method. I thougth that component (in my case — bevel) holds its state unchanged until I'm draw something on it.
When I tried to rewrite it to draw in the onpaint method I'm stuck with the problem, reproduce it with the minimal code and came here for advice. :)

Now I know that the components redraw itselves on every sneeze. God damn it! :D

By the way if so what's the difference between Bevel and PaintBox?
If every component redraws itself on every move so I can use any as a layer for the mine field, even a button (if I somehow override reacting on clicking).

TRon

  • Hero Member
  • *****
  • Posts: 3923
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #32 on: January 23, 2025, 06:32:13 pm »
Googling and reading this topic gives me understanding that
Although you can use google, the help is a much better source. It is just a press to F1 away  :)

As a good second alternative, the online documentation as well as dsiders nextdoc documentation

Still have questions, then there is the wiki with a plethora of topics and examples. Speaking of which the Lazarus IDE and its component comes loaded with examples

fwiw in particular the wiki article developing with graphics, if even to get an idea of the possibilities that are available.

I am simply mentioning all of them in case you are not aware, in case you already are then please ignore.

Ofc. when in doubt you can always post your questions but as an genuine advise, it should not be your first resort.

refresh — refreshes the component internally without repainting it;
TControl.Refresh:
Quote
Calls the Repaint method to either draw the clipping rectangle for the control to the handle in the Parent, or invalidate the control area for the next paint operation that occurs when the Parent controls are Visible.

invalidate — tells the component that its visuals is outdated and then repaint only visible part of component (maybe some part off screen or cover by another window, whatever)
Invalidate
Quote
Calls the InvalidateControl method to invalidate the bounds rectangle for the control using the clipping rectangle for the Parent. The control is redrawn when there are no pending messages in the message queue.
How exactly it is implemented depends on the used control. But yes in basics what you wrote applies.

update — refresh + repaint;

update
Quote
Update calls the Update method in the Parent control to refresh the window where the control is hosted. No actions are performed in the method when Parent has not been assigned.
Thus, the parent gets notified. What the parent does (in relation to the component that was placed on it and notified it) depends on the parent.

// there is some unclearness: are refresh and update not telling the component that its visuals is outdated? What's the point?
In case the former, yes. In case the latter it depends on the parent component. But it also depends on how the child-control handles 'instructions' from the parent. It can vary per control.

repaint — just repaint
Correct.

// and there is some unclearness too: what's the difference between calling Repaint and calling OnPaint method? For example, PaintBox1.Repaint or PaintBox1.OnPaintDoSomeStuff.
You do not call the onpaint method. It is an event that is invoked by the control (when assigned) whenever it it deems necessary to do so while invoking paint or repaint is something that you are able to call whenever you feel the need to do so.

When the event exactly is invoked depends on how a control implements it but basically it does this behind the scenes so that you do not have to worry about the order in which things need to happen for the control to visually presents itself (correctly).

Calling a method directly, could imply you have to wait for the method to finish while other methods uses messaging in order to eventually invoke the (assigned) event handler(s).

No, we shouldn't. If there are some other already opened cells drawing the complete grid will override it.
Not if you keep track of the state, that is the whole point I was trying to make. The state determines how a cell should be drawn.

To make an analogy, it seems to me like you view this as a piece of paper and a pencil with an eraser where each next step you add or remove something from the paper. That is the way as suggested by ASerge by using a off-screen bitmap.

However, the way you have implemented it in your example requires to redraw the whole grid for each change that was made to the grid.

Both solutions are valid but also both have their pros and cons.

n my real program I'm doing exactly how you mentioned beneath — working with the dynamic array of cells and repainting (or not, if it's already opened) only one that clicked.
Actually I was a fool and doing all my drawings in the onclick method. I thougth that component (in my case — bevel) holds its state unchanged until I'm draw something on it.
When I tried to rewrite it to draw in the onpaint method I'm stuck with the problem, reproduce it with the minimal code and came here for advice.
As stated /if/ you keep using the path as in your example (nothing wrong with it, just making sure you get the idea) then get rid of the drawbomb check and the click check and draw the whole grid (e.g. every cell). Use a state for each cell to know exactly what you need to draw (cell open, cell closed, etc.). When you left click the mouse then change the state of the cell before drawing/updating/refreshing the grid.

In case you only wish to draw the changes in opposite to the method above then make use of an off-screen bitmap and in the onpaint event draw that bitmap to the canvas of the paintbox.

Hopefully that was able to clarify a few things, if not then don't hesitate to point them out.

edit: typos and hopefully better distinction between quotes.
« Last Edit: January 23, 2025, 06:49:03 pm by TRon »
I do not have to remember anything anymore thanks to total-recall.

majolika

  • New Member
  • *
  • Posts: 41
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #33 on: January 23, 2025, 07:13:20 pm »
Although you can use google, the help is a much better source. It is just a press to F1 away :-)
Thanks for your paitience!
I'm not a newbie in programming itself but I'm total newbie in LCL so a couple of things can be unclear and challenging for me. :)
And of course I'm not an expert, programming is just a hobby for me. Sort of. :)

Hopefully that was able to clarify a few things, if not then don't hesitate to point them out.
Sure it was! Thanks again!

I redwrote my example to use bitmap as an intermediate layer and it works well.
Only one thing seems to me not the best and elegant way to do it.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FieldInit(Cols, Rows: Integer);
  2. begin
  3.   ...
  4.   if FieldBitmap <> nil then begin
  5.      FieldBitmap.Destroy;
  6.      FieldBitmap := TBitmap.Create;
  7.   end;
  8.   ...
  9. end;
  10.  
  11. procedure TForm1.FormCreate(Sender: TObject);
  12. begin
  13.   FieldBitmap := TBitmap.Create;
  14. end;

I'm just recreating the bitmap on every restart but it feels unnatural.
To my confuse just repaint doesn't work.

Renewed example attached so you can play around with it.

TRon

  • Hero Member
  • *****
  • Posts: 3923
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #34 on: January 23, 2025, 07:25:43 pm »
I was just on my way heading out so it might take me a while to reply. Post noted and I will have a look when I'm back. But perhaps someone else is able to address your concerns/question(s) as well.
I do not have to remember anything anymore thanks to total-recall.

majolika

  • New Member
  • *
  • Posts: 41
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #35 on: January 23, 2025, 07:29:27 pm »
I was just on my way heading out so it might take me a while to reply. Post noted and I will have a look when I'm back. But perhaps someone else is able to address your concerns/question(s) as well.
I'm not in a hurry! Good luck out there!

majolika

  • New Member
  • *
  • Posts: 41
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #36 on: January 23, 2025, 10:07:31 pm »
Offtopic

I was playing around with Form Designer for better understanding Aligniing and Anchoring mechanics and I think I found a bug in Docked Form Editor. Very easy to reproduce.

For now I use Lazarus 3.6 and I think I have to update to 3.8. Maybe this bug is already corrected in 3.8. But I'm not sure how to do it properly.

TRon

  • Hero Member
  • *****
  • Posts: 3923
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #37 on: January 23, 2025, 10:52:32 pm »
Thanks for your paitience!
To show incentive is half the job  :)

Quote
I'm not a newbie in programming itself but I'm total newbie in LCL so a couple of things can be unclear and challenging for me. :)
I understand and that shouldn't be a problem.

However, and try keep that in mind if you are able, although in basics components work in a similar fashion the details will differ for each individual component.

That is why it is relatively difficult to get the hang of it because the only way to figure that out is by trying... lots of trying.

Quote
And of course I'm not an expert, programming is just a hobby for me. Sort of. :)
Did we not all started out that way ?

I consider myself a newbie as well even though I have been using FPC and Lazarus for decades. Both lazarus/LCL and FPC/RTL/FCL are a moving target and get new features with each release.


As a hint (take it or leave it as you wish):
I personally treat every component that I am not familiar with as a Christmas present that requires careful unwrapping.

In that regards when start using a new component (that I are unfamiliar with) in an existing project, I never do that right away but experiment with the component first and try figure out that the functionality that my project requires is actually feasible to accomplish with that component. If the components behaves or acts in a way that makes my project more difficult to maintain or to even implement the use of that component then I seek elsewhere or in case a bit more familiar with the component try to change the default behaviour to match the requirements for my project. Worse case scenario requires to create my own component.

The unwrapping sequence allows me to get familiar with the component, get to know it in general but especially its quirks, know how it behaves for different widgetsets and platforms.

As you can imagine that takes time and for sure I have not even touched a quarter of the existing components out there. Therefor I consider that to not have knowledge on any of those other than what I am able to read in the documentation and/or wiki and/or am able to check with existing examples. Questions asked in the forums are sometimes helpful as well especially when there are hardly any examples present and/or the components are undocumented (sometimes even requires to dive into their sources in order to be able to see how it is suppose to behave).

Things get easier the longer you play around with Lazarus and its components. It is a matter of how much time do you wish to invest.

And that answer is different for each individual. Some are able to get by using some buttons, labels and panels with their basic functionality while others require reporting components, database access and 3D drawing/editing controls with detailed modified behaviour to match their project's requirements.

fwiw: There are even individuals that do not get all of this, try the shortcut option and just start copying over other people's snippets, post them on the forum reporting a prolem in the hopes someone else will solve their issues so that they do not have to (they are easy to spot though).

Back to topic.

Quote
I redwrote my example to use bitmap as an intermediate layer and it works well.
Yes, I can see. That is nice progress.

I do have a couple of hints but remember that no matter what anyone tries to tell you (unless you are ofc really in the wrong) how you implement
things can be a topic of discussion but in the end if it works (for you) then there is no right nor wrong.

Quote
Only one thing seems to me not the best and elegant way to do it.
Ok, let's try provide some pointers. Keep my previous remark in mind.

The bitmap variable in the source is positioned between the components. It is wiser to situate that into a separate section. If you do not have to access that field outside your form then it is custom to make it private (but you could just as well make it part of your other variable declaration block).

Speaking of which. All variables declared right under the form variable declaration are accessible outside your form units as well. It is custom to make these variables part of a private section in your form type declaration.

If you must place them outside your form type declaration it is custom to do that as part of the implementation section. It is rare though but I understand and on occasion use it myself as well in testing phase.

The bitmap can be resized and also be cleared. Therefor there is no actual need to create the bitmap over and over again. You can create it once at form create event and destroy (free) it once at form destruction event.

Other than that I could perhaps start nitpicking on a dozen of other things but it looks as you seem to be doing well. If it works, it works  :D

I rather wait for your epiphany moment regarding cell status (or perhaps you might surprise us with something clever)   :P

Keep it up I would say as you are on the right track !
I do not have to remember anything anymore thanks to total-recall.

majolika

  • New Member
  • *
  • Posts: 41
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #38 on: January 24, 2025, 12:30:40 am »
I personally treat every component that I am not familiar with as a Christmas present that requires careful unwrapping.
That's a good idea!

I do have a couple of hints
And I'm here to get some!

The bitmap variable in the source is positioned between the components.
It is wiser to situate that into a separate section.
You right! I just placed it right after form variables keeping in mind only one thing: it sholud have access to other form's vars and methods.
I moved it to private section now but I thibk it doesn't matter for such a small educational project like mine.

Speaking of which. All variables declared right under the form variable declaration are accessible outside your form units as well. It is custom to make these variables part of a private section in your form type declaration.
You right. But what if I make another form (for example, Options form to change mine field sizes with sliders and see how the mine field is changing sizes in real time)? Option form will not have access to private section of Main form. So some vars should be global.

If you must place them outside your form type declaration it is custom to do that as part of the implementation section.
Correct me if I'm wrong.
All that is placed in the interface section are accessible from another units and forms.
All that is placed in the implementation section are not accessible from another units and forms.
This is how I can recall it from my Borland Pascal times. :)

The bitmap can be resized and also be cleared.
TBitmap.Clear! Damn it! So easy!

I rather wait for your epiphany moment regarding cell status.
:D
Cell status is not a problem: I'm already using 2 arrays of integer to manage that: one array — with bombs and neighbours (I decided to calculate neighbors one time at a randomizing stage but this is a subject to change), another — with cell statuses.
Of cource I can use only one array of bytes to minimize using memory but it will cost more annoying calculations, bitmasks... I don't like that.
And I hope I will never be in need of a million×billion-sized mine fiield. :)

First of all  I want to make things as simple as I can.
Initially I want to make program with basic functionality keeping in mind future functionality.
For example now I randomize the mine field in just 4 code lines: 1 - get random col 2 - get random row, 3 - check if there's already a bomb, 4 - set bomb and atatus in my 2 arrays.
But later I want to make more complex randomization to avoid unsolveable fields. Later. For now let the things be simple but extendable.

Maybe I should start another topic for qustions which definitly will come up...
E.g. aligning and anchoring... I just touched water but see a lot of underwater stones.

TRon

  • Hero Member
  • *****
  • Posts: 3923
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #39 on: January 24, 2025, 06:38:20 am »
You right! I just placed it right after form variables keeping in mind only one thing: it sholud have access to other form's vars and methods.
I moved it to private section now but I thibk it doesn't matter for such a small educational project like mine.
You're probably right.

Like most people I do not like mixing IDE generated content with my own especially since it has the habit of being tossed around when adding and removing components.

In light of your other remark(s) it is quite possible to add a class visibility specifier multiple times in a class type definition, for instance the public specifier.

Quote
You right. But what if I make another form (for example, Options form to change mine field sizes with sliders and see how the mine field is changing sizes in real time)? Option form will not have access to private section of Main form. So some vars should be global.
You are quite right about that. I assumed (yes, mother of all screw-ups  :D ) it was a simple implementation contained in a single unit/form (perhaps using menu's and/or other components on the same form).

Besides that, it is also possible to pass along variables to another procedure/function/method so that its value can be changed outside it's normal scope. You can also do that with a bunch of variables tucked away inside a record, object or class therefor it is not always necessary to have direct access to variables. Getting really fancy you can also pass along messages between different forms to request (which you would manually have to handle) a change of value for a variable thought that would be really overkill.

Quote
Correct me if I'm wrong.
All that is placed in the interface section are accessible from another units and forms.
All that is placed in the implementation section are not accessible from another units and forms.
This is how I can recall it from my Borland Pascal times.
If you are talking about direct access then yes you are absolutely correct (see above remark about passing along)

With regards to your other remarks about the game you can implement things using different methods. Whatever works best for you would be the correct implementation  ;D

Quote
Of cource I can use only one array of bytes to minimize using memory but it will cost more annoying calculations, bitmasks... I don't like that.
Instead of bitmasks, have a look at a set (enums). If my calculations are correct then the maximum number of neighbouring bombs would be eight (nine if you count zero). Add to that whether or not a cell contains a bomb or a flag perhaps a question-mark and you would be well on your way.

But as said, whatever works best for you is the way to go.

Quote
E.g. aligning and anchoring... I just touched water but see a lot of underwater stones.
Ah yes you mentioned having an issue with that. Note that there were quite a few changes/fixes with regards to anchors and alignments since the release you seem to be using so it might very well you encountered a bug (that might also have been fixed in the mean time). With the information provided (unless I missed something) it is quite impossible to reproduce/verify.

If you are able to explain what you are trying to achieve and what fails (e.g. what in your eyes is a (reproducable) bug) then we can have a look at it.

Also note that Lazarus 4.0RC2 also exist and that has even more fixes in comparison to 3.8 (though I am not sure how this relates wrt the few days ago released 3.8 fixes release).

Whether or not you wish to pursue that anchor issue in this same thread is up to you. If you wish to focus on a particular issue and perhaps try attract more readers then it might be helpful to start another thread solely about that topic but things always seem to have a tendency to work out differently as expected/intended.
I do not have to remember anything anymore thanks to total-recall.

majolika

  • New Member
  • *
  • Posts: 41
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #40 on: January 24, 2025, 10:26:08 am »
Like most people I do not like mixing IDE generated content with my own
Me too but it's not a big deal for a small educational projects like mine. And of course visual designer might be quite useful.

Quote
especially since it has the habit of being tossed around when adding and removing components.
Right! So I decided to write messy code at first and refactor it only when I'm done.

Quote
In light of your other remark(s) it is quite possible to add a class visibility specifier multiple times in a class type definition, for instance the public specifier.
Somthing like:   private {bla-bla-bla} public {bla-bla-bla} private {bla-bla-bla} public {bla-bla-bla} ?
Looks like useful but bad code practice.

Quote
I assumed (yes, mother of all screw-ups  :D ) it was a simple implementation contained in a single unit/form (perhaps using menu's and/or other components on the same form).
For now it is. But I write a code keeping in mind that functionality of my program might extend. At the same time I don't want to make my code overcomplicated (like passing massages with manually handling). No doubt there's a different ways to passing vars and using globals is not the best practice. But for a small project that's enough. I assume (mother of all screw-ups :D ) that there are no penalties or issues with using globals for — again! — a small project.
But I appreciate that you share this knowledge with me. It might be quite useful if I'm gonna make a big, serious project.

Quote
Whatever works best for you would be the correct implementation  ;D
I hope so! :)

Quote
Instead of bitmasks, have a look at a set (enums). If my calculations are correct then the maximum number of neighbouring bombs would be eight (nine if you count zero). Add to that whether or not a cell contains a bomb or a flag perhaps a question-mark and you would be well on your way.
Yes, It was my first attempt but lately I'll show you why I decided not to use it.

Quote
If you are able to explain what you are trying to achieve and what fails (e.g. what in your eyes is a (reproducable) bug) then we can have a look at it.
Ok, I'll explain and attach some screenshots.
First of all, I use Lazarus 3.6 with AnchorDockingDsgn and DockedFormEditor packeges incorporated. Windows 10.
I was playing around with a newly created project to see how anchoring, aligning and autosizing works.
So here's the minimal steps to reproduce:

1. Create new project;
2. In the Form Designer place TPaintBox on an empty form;
3. Set AutoSize property of TForm to True by checking it in the Object Inspector.
4. Ka-boom!

Have a look to attached screenshots to see what happens.

I think that the bug is inside DockedFormEditor because if I remove this package and rebuild IDE I'm no longer able to reproduce it.
But I don't want to remove this package, it's very handy and useful for me.
For now I don't need TForm.AutoSize property or I can set it in a code if I needed. But the bug is the bug.

Quote
Also note that Lazarus 4.0RC2 also exist and that has even more fixes in comparison to 3.8 (though I am not sure how this relates wrt the few days ago released 3.8 fixes release).
I prefer to use stable versions of products so betas or RCs is not an option. Only in special cases.
But it would be nice if you give me an advice on how to [properly update 3.6 to 3.8.

Quote
If you wish to focus on a particular issue and perhaps try attract more readers then it might be helpful to start another thread solely
I think later I'll start a new topic with subject something like "Stupid questions of a newbie making a sort of Mine Sweeper". :)

Quote
things always seem to have a tendency to work out differently as expected/intended.
Story of my life :D

majolika

  • New Member
  • *
  • Posts: 41
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #41 on: January 24, 2025, 11:06:26 am »
2. In the Form Designer place TPaintBox on an empty form;
3. Set AutoSize property of TForm to True by checking it in the Object Inspector.
Steps 2 and 3 can be reversed and instead of TPaintBox it can be used another non-visual components. Not all.
For example, TOpenDialog doesn't cause any trouble but TImage, TShape — do.
I didn't test all available components but I hope three (TPaintBox, TImage, TShape) is enough to locate the bug.

TRon

  • Hero Member
  • *****
  • Posts: 3923
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #42 on: January 25, 2025, 06:26:57 pm »
Ok, I'll explain and attach some screenshots.
Thank you. Nice and clear description.

Problem (for me) is that I do not run windows and the issue is non reproducible for me on Linux/GTK. I was unable to locate a mention of this particular problem in the bugtracker (but I might just as well have overlooked or misunderstood the bugtracker description(s)).

Therefor I am unable to guide you further on that topic other then to advise to try using a newer version of the IDE (either 3.8 or 4.0RC2) in order to see if the issue is still present.
I do not have to remember anything anymore thanks to total-recall.

majolika

  • New Member
  • *
  • Posts: 41
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #43 on: January 25, 2025, 06:34:11 pm »
try using a newer version of the IDE (either 3.8 or 4.0RC2)
The question is how I update properly? Fully uninstall and install new version? But how can I save all settings of IDE?

TRon

  • Hero Member
  • *****
  • Posts: 3923
Re: Painting on a control - 3 questions: OnPaint, ImageList, program logic
« Reply #44 on: January 25, 2025, 06:51:38 pm »
The question is how I update properly? Fully uninstall and install new version? But how can I save all settings of IDE?
That is a very good question because I am not familiar with the windows side of things (e.g. the installer).

It is quite possible to run different installations next to each other but as far as I know the windows version comes distributed only with the installer version. I have seen people mention that the installer automatically takes care of things (asking questions if you want to use the same configuration or not) but it is hearsay so don't take it for truth).

Personally I build Lazarus from source and "install" it manually.

In case you wish to preserve your configuration (something that Lazarus should ask if it uses you old settings) make sure to backup your Lazarus configuration directory just in case something does go amiss. It contains all the settings and in case having used OPM all your OPM installed components. You have to note that it is possible that once you have updated your old configuration files that it might happen you are not able to revert back these settings due to incompatibilities (new introduced features etc).

I am not sure what would be the best advise for your situation. Sorry.

edit: If you really rely on your current lazarus version for production then the safest would be to test a newer version inside a VM where it doesn't matter when things goes wrong.
« Last Edit: January 25, 2025, 06:56:10 pm by TRon »
I do not have to remember anything anymore thanks to total-recall.

 

TinyPortal © 2005-2018