Recent

Author Topic: Progress bar in stringgrid  (Read 15221 times)

Tony Stone

  • Full Member
  • ***
  • Posts: 219
Progress bar in stringgrid
« on: July 13, 2021, 03:06:14 pm »
Anyone know of any Stringgrid components where you can put a progress bar in a cell?  Or any suggestions on making a progress bar display over a cell... I mean it's obviously possible... I looked into it a little bit and it seems I would need to do quite a large amount of coding for something that isn't really so important to my project.  Would I be best off creating a new custom stringgrid component for myself?
« Last Edit: July 13, 2021, 10:43:29 pm by Tony Stone »

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: Progress at in stringgrid
« Reply #1 on: July 13, 2021, 03:27:08 pm »
No need for a dedicated StringGrid.

Simply add a TProgressbar to the form. Set its Visible property to false in order to hide it. Then, when you need it in the grid, set its parent to the grid (Progressbar1.Parent := StringGrid1) and show it (Progressbar1.Show). You will also have to set the bounds of the progressbar equal to the cell in which it will be shown (R := StringGrid1.CellRect(2, 1), where R is a TRect, and (2, 1) are the Col/Row coordinates of the cell. Then: Progressbar1.SetBounds(R.Left, R.Top, R.Right-R.Left, R.Bottom-R.Top)). When the process has come to an end hide the progressbar again.

This works for me:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   R: TRect;
  4.   i: Integer;
  5. begin
  6.   Progressbar1.Min := 0;
  7.   Progressbar1.Max := 100;
  8.   Progressbar1.Position := 0;
  9.  
  10.   R := StringGrid1.CellRect(2, 1);
  11.   Progressbar1.Setbounds(R.Left, R.Top, R.Right-R.Left, R.Bottom-R.Top);
  12.   Progressbar1.Parent := StringGrid1;
  13.   Progressbar1.Show;
  14.  
  15.   for i := 0 to 100 do
  16.   begin
  17.     StringGrid1.Cells[1, 1] := IntToStr(i);
  18.     Progressbar1.Position := i;
  19.     Application.ProcessMessages;
  20.     Sleep(10);
  21.   end;
  22.  
  23.   Progressbar1.Hide;
  24. end;

I noticed that the Windows Progressbar is not in sync with the real progress - this looks a bit strange. And in Linux I noticed that the progress is not always displayed. So still some field of experimentation. If it does not work out at all you'll have to owner-draw some kind of progress bar in the cell directly (in this case, ask again).
« Last Edit: July 13, 2021, 03:40:39 pm by wp »

alpine

  • Hero Member
  • *****
  • Posts: 1063
Re: Progress at in stringgrid
« Reply #2 on: July 13, 2021, 03:33:05 pm »
Anyone know of any stringgrid components where you can put a progress at in a cell?  Orany suggestions on making a progress at display over a cell... I mean it's obviously possible... I looked into it a little bit and it seems I would need to do quite a large amount of coding for something that isn't really so important to my project.  Would I be best off creating a new custom stringgrid component for myself?
Not at all. Just use TStringGrid.OnDrawCell event and draw a filled rectangle, TStringGrid.Canvas.FillRect(ARect.Left, ARect.Top, ARect.Left + Round(ARect.Width * PercentageDone/100), ARect.Top + ARect.Height). Don't forget to call TStringGrid.InvalidateCell() when you want it repainted. You can use also Canvas.TextOut() to put the progress message.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

Tony Stone

  • Full Member
  • ***
  • Posts: 219
Re: Progress at in stringgrid
« Reply #3 on: July 14, 2021, 11:12:22 pm »
Anyone know of any stringgrid components where you can put a progress at in a cell?  Orany suggestions on making a progress at display over a cell... I mean it's obviously possible... I looked into it a little bit and it seems I would need to do quite a large amount of coding for something that isn't really so important to my project.  Would I be best off creating a new custom stringgrid component for myself?
Not at all. Just use TStringGrid.OnDrawCell event and draw a filled rectangle, TStringGrid.Canvas.FillRect(ARect.Left, ARect.Top, ARect.Left + Round(ARect.Width * PercentageDone/100), ARect.Top + ARect.Height). Don't forget to call TStringGrid.InvalidateCell() when you want it repainted. You can use also Canvas.TextOut() to put the progress message.

So I tried to implement this last night.  And I ran into some issues.  It was actually drawing over every item in the Stringgrid... And turning the top row green. Lol... So while I thought your solution was perfect it needs some work.  I'm gonna get back on later and put together a quick demo app showing what I did and maybe you can take a look and see where I went wrong.  Or maybe I will figure it out putting a demo app together.  I am just wondering why to put it in the ondraw event.  Wouldn't I need to have a procedure of my own telling it to draw in cell xy...  Your solution caused me a lot.of.confusion...  I was not able.to grasp where the trect properties are coming from... I tried a few different things before calling the ondraw event but it just made the Stringgrid stop displaying headers and anything else properly... Text was being chopped apart etc.  I will put together my demo in a couple hours here.  Thanks for your suggestions either way!

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1314
    • Lebeau Software
Re: Progress at in stringgrid
« Reply #4 on: July 14, 2021, 11:43:02 pm »
Just use TStringGrid.OnDrawCell event and draw a filled rectangle

That is the way I prefer to do it.  Though, I find an owner-drawn TListView in vsReport mode tends to look better (ie, more modern) than an owner-drawn TStringGrid, but that is just my opinion.

So I tried to implement this last night.  And I ran into some issues.  It was actually drawing over every item in the Stringgrid... And turning the top row green. Lol... So while I thought your solution was perfect it needs some work.  I'm gonna get back on later and put together a quick demo app showing what I did and maybe you can take a look and see where I went wrong.  Or maybe I will figure it out putting a demo app together.

You probably didn't take the aCol/aRow parameters into account correctly so you would draw only in specific cells that need a progress bar drawn in them, while skipping cells that don't.

I am just wondering why to put it in the ondraw event.

Doing so uses fewer system resources, and keeps the progress bar tied to the cell it belongs to.  Using a TProgressBar like wp described requires a dedicated HWND per TProgressBar, and if the TStringGrid is scrolled then the TProgressBar has to be moved accordingly, etc.

Wouldn't I need to have a procedure of my own telling it to draw in cell xy...

You could, if you want to.  For instance, if you have a 2D array of progress values, you can use the cell indexes provided by the OnDrawCell event to index into the array, and then draw progress only for those cells that have progress to display.  Such a function can update the appropriate array element, then InvalidateCell() the corresponding cell, so the next OnDrawCell event for that cell will draw/skip an updated progress bar.

I was not able.to grasp where the trect properties are coming from...

The provided TRect is the rectangle of the cell that is currently being drawn in the grid's client area.  In the OnDrawCell event, you can do whatever drawing you want within the bounds of the provided rectangle.

I tried a few different things before calling the ondraw event

You are not supposed to cell the event yourself.  It is called automatically while the grid is being painted onscreen.
« Last Edit: July 14, 2021, 11:45:31 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

Tony Stone

  • Full Member
  • ***
  • Posts: 219
Re: Progress bar in stringgrid
« Reply #5 on: July 15, 2021, 12:57:56 am »
Well... I am taking in the previous answers from Remy right now and will get back into it in a minute.  But here is what I slapped together... which obviously isnt working.  I think Remy gave me enough info to correct the issues.  I will report back shortly!  :D

EDIT:

I just changed line 74 to PERCENTDONEsingle:=TrackBar1.Position;

just to eliminate the progress calculator function i am using in my other program... I should have left it out.
« Last Edit: July 15, 2021, 01:03:42 am by Tony Stone »

Tony Stone

  • Full Member
  • ***
  • Posts: 219
Re: Progress bar in stringgrid
« Reply #6 on: July 15, 2021, 01:26:12 am »
Remy, I also see there is a canvas.grafientfill procedure... I am wondering if this would be a little easier to use?  I am messing with it now but not really having much success.

alpine

  • Hero Member
  • *****
  • Posts: 1063
Re: Progress bar in stringgrid
« Reply #7 on: July 15, 2021, 02:00:16 am »
@Tony Stone
It seems we're in areas very far apart, I desperately need some sleep right now... but just a few things at first glance

The paint process is orchestrated by the Windows itself, you should do all painting into the OnDrawCell handler. Including FillRect, TextOut or whatever else you want to do with the canvas. You must restrict yourself into the boundaries of the given ARect. Otherwise you'll paint a mess.

The paint process is triggered when you call InvalidateCell(). This is the only method you should call outside the OnDrawCell.

As Remy noted, you must take in account ACol/ARow arguments of OnDrawCell and eventually AState - it will tell you if the cell is fixed, selected, etc. Otherwise you'll paint on every cell! (OnDrawCell is called at least once for every visible cell in the grid) 


"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1314
    • Lebeau Software
Re: Progress bar in stringgrid
« Reply #8 on: July 15, 2021, 02:14:13 am »
here is what I slapped together... which obviously isnt working.

Like I said, you are completely ignoring the aCol/aRow parameters that the OnDrawCell event gives you.  Try something more like this instead:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, Grids, ComCtrls,
  9.   StdCtrls, Types;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     edtFileName: TEdit;
  17.     StringGrid1: TStringGrid;
  18.     TrackBar1: TTrackBar;
  19.     procedure FormCreate(Sender: TObject);
  20.     procedure StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
  21.       aRect: TRect; aState: TGridDrawState);
  22.     procedure TrackBar1Change(Sender: TObject);
  23.     function getSingleProgress(Pos64, Max64: Int64): Integer;
  24.   private
  25.     Progress: array of Integer;
  26.   public
  27.  
  28.   end;
  29.  
  30. var
  31.   Form1: TForm1;
  32.  
  33. implementation
  34.  
  35. {$R *.lfm}
  36.  
  37. { TForm1 }
  38.  
  39. procedure TForm1.FormCreate(Sender: TObject);
  40. var
  41.   i: integer;
  42. begin
  43.   SetLength(Progress, 11);
  44.   for i := 0 to 10 do
  45.   begin
  46.     Progress[i] := -1;
  47.     StringGrid1.InsertRowWithValues(StringGrid1.RowCount,
  48.         ['filename'+IntToStr(i), '1.5GB', 'Tab', '0', '0', '1', '$100.00', 'NSF,CLOSED', '1', 'READY', '0']);
  49.   end;
  50. end;
  51.  
  52. procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
  53.   aRect: TRect; aState: TGridDrawState);
  54. var
  55.   R: TRect;
  56.   Index: Integer;
  57. begin
  58.   if (aCol = 9) and (aRow >= StringGrid1.FixedRows) then begin
  59.     Index := aRow - StringGrid1.FixedRows;
  60.     if Progress[Index] >= 0 then begin
  61.       // draw the progress bar as needed...
  62.       StringGrid1.Canvas.Brush.Color := StringGrid1.Color;
  63.       StringGrid1.Canvas.FillRect(aRect);
  64.       StringGrid1.Canvas.Font.Color := StringGrid1.Font.Color;
  65.       StringGrid1.Canvas.TextRect(aRect, aRect.Left + 2, aRect.Top + 2, 'working...');
  66.       R := aRect;
  67.       R.Right := R.Left + Round(R.Width * Progress[Index]/100);
  68.       StringGrid1.Canvas.Brush.Color := clGreen;
  69.       StringGrid1.Canvas.FillRect(R);
  70.       StringGrid1.Canvas.Font.Color := clWhite;
  71.       StringGrid1.Canvas.TextRect(R, R.Left + 2, R.Top + 2, 'working...');
  72.       Exit;
  73.     end;
  74.   end;
  75.   StringGrid1.DefaultDrawCell(aCol, aRow, aRect, aState);
  76. end;
  77.  
  78. procedure TForm1.TrackBar1Change(Sender: TObject);
  79. var
  80.   i: Integer;
  81. begin
  82.   for i := StringGrid1.FixedRows to StringGrid1.RowCount-1 do begin
  83.     if StringGrid1.Cells[0, i] = edtFileName.Text then begin
  84.       Progress[i - StringGrid1.FixedRows] := getSingleProgress(TrackBar1.Position, 5641541654);
  85.       StringGrid1.InvalidateCell(9, i);
  86.       break;
  87.     end;
  88.   end;
  89. end;
  90.  
  91. function TForm1.getSingleProgress(Pos64, Max64: Int64): Integer;
  92. begin
  93.   Result := Integer(Trunc((Pos64 / Max64) * 100));
  94. end;
  95.  
  96. end.
« Last Edit: July 15, 2021, 05:33:48 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

Tony Stone

  • Full Member
  • ***
  • Posts: 219
Re: Progress bar in stringgrid
« Reply #9 on: July 15, 2021, 03:30:27 am »
Welp, all I can say is you guys are good.  I do have it mostly working now.  I am starting to understand the way this works.  So a TRect is just a object with a top, left, width and height property?   So a shape... a rectangle...?

I also got a compile error right away on defaultdrawcell procedure about not enough arguments so I changed it to this:
StringGrid1.DefaultDrawCell(aCol, aRow,aRect,aState);

Seems to all work in the little demo project I started.  Running into issues implementing it in my actual project though because i created a program that is so overly complicated, has so many moving parts and it takes me a long time to implement little things.  My goal for the next project is to learn better coding organization and practices to keep things cleaner.  I hope some day i can whip up some sample code as fast as you guys have!

lucamar

  • Hero Member
  • *****
  • Posts: 4219
Re: Progress bar in stringgrid
« Reply #10 on: July 15, 2021, 05:30:46 am »
So a TRect is just a object with a top, left, width and height property?   So a shape... a rectangle...?

TRect is an advanced record with, basically, four LongInts (or two TPoints) for the corners and a bunch of "methods" and "properties" to make it easier to deal with. So yeah, almost, but not quite, just an "object" with Top,  Left, etc. properties/methods. ;)
Turbo Pascal 3 CP/M - Amstrad PCW 8256 (512 KB !!!) :P
Lazarus/FPC 2.0.8/3.0.4 & 2.0.12/3.2.0 - 32/64 bits on:
(K|L|X)Ubuntu 12..18, Windows XP, 7, 10 and various DOSes.

Zvoni

  • Hero Member
  • *****
  • Posts: 2327
Re: Progress bar in stringgrid
« Reply #11 on: July 15, 2021, 09:06:30 am »
here is what I slapped together... which obviously isnt working.

Like I said, you are completely ignoring the aCol/aRow parameters that the OnDrawCell event gives you.  Try something more like this instead:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, Grids, ComCtrls,
  9.   StdCtrls, Types;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     edtFileName: TEdit;
  17.     StringGrid1: TStringGrid;
  18.     TrackBar1: TTrackBar;
  19.     procedure FormCreate(Sender: TObject);
  20.     procedure StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
  21.       aRect: TRect; aState: TGridDrawState);
  22.     procedure TrackBar1Change(Sender: TObject);
  23.     function getSingleProgress(Pos64, Max64: Int64): Integer;
  24.   private
  25.     Progress: array of Integer;
  26.   public
  27.  
  28.   end;
  29.  
  30. var
  31.   Form1: TForm1;
  32.  
  33. implementation
  34.  
  35. {$R *.lfm}
  36.  
  37. { TForm1 }
  38.  
  39. procedure TForm1.FormCreate(Sender: TObject);
  40. var
  41.   i: integer;
  42. begin
  43.   SetLength(Progress, 11);
  44.   for i := 0 to 10 do
  45.   begin
  46.     Progress[i] := -1;
  47.     StringGrid1.InsertRowWithValues(StringGrid1.RowCount,
  48.         ['filename'+IntToStr(i), '1.5GB', 'Tab', '0', '0', '1', '$100.00', 'NSF,CLOSED', '1', 'READY', '0']);
  49.   end;
  50. end;
  51.  
  52. procedure TForm1.StringGrid1DrawCell(Sender: TObject; aCol, aRow: Integer;
  53.   aRect: TRect; aState: TGridDrawState);
  54. var
  55.   R: TRect;
  56.   Index: Integer;
  57. begin
  58.   if (aCol = 9) and (aRow >= StringGrid1.FixedCols) then begin
  59.     Index := aRow - StringGrid1.FixedCols;
  60.     if Progress[Index] >= 0 then begin
  61.       StringGrid1.Canvas.Brush.Color := StringGrid1.Color;
  62.       StringGrid1.Canvas.FillRect(aRect);
  63.       StringGrid1.Canvas.Font.Color := StringGrid1.Font.Color;
  64.       StringGrid1.Canvas.TextRect(aRect, aRect.Left + 2, aRect.Top + 2, 'working...');
  65.       R := aRect;
  66.       R.Right := R.Left + Round(R.Width * Progress[Index]/100);
  67.       StringGrid1.Canvas.Brush.Color := clGreen;
  68.       StringGrid1.Canvas.FillRect(R);
  69.       StringGrid1.Canvas.Font.Color := clWhite;
  70.       StringGrid1.Canvas.TextRect(R, R.Left + 2, R.Top + 2, 'working...');
  71.       Exit;
  72.     end;
  73.   end;
  74.   StringGrid1.DefaultDrawCell(aCol, aRow);
  75. end;
  76.  
  77. procedure TForm1.TrackBar1Change(Sender: TObject);
  78. var
  79.   i: Integer;
  80. begin
  81.   for i := StringGrid1.FixedCols to StringGrid1.RowCount-1 do begin
  82.     if StringGrid1.Cells[0, i] = edtFileName.Text then begin
  83.       Progress[i - StringGrid1.FixedCols] := getSingleProgress(TrackBar1.Position, 5641541654);
  84.       StringGrid1.InvalidateCell(9, i);
  85.       break;
  86.     end;
  87.   end;
  88. end;
  89.  
  90. function TForm1.getSingleProgress(Pos64, Max64: Int64): Integer;
  91. begin
  92.   Result := Integer(Trunc((Pos64 / Max64) * 100));
  93. end;
  94.  
  95. end.

Remy, your Lines 58 and 59: Shouldn't that be FixedRows instead of FixedCols??
I admit, haven't looked too closely at the code (or even tried it), but it just threw me that you compare a row with a Col-Property
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1314
    • Lebeau Software
Re: Progress bar in stringgrid
« Reply #12 on: July 15, 2021, 05:35:12 pm »
I also got a compile error right away on defaultdrawcell procedure about not enough arguments so I changed it to this:
StringGrid1.DefaultDrawCell(aCol, aRow,aRect,aState);

Fixed.

Remy, your Lines 58 and 59: Shouldn't that be FixedRows instead of FixedCols??

Fixed.
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

Tony Stone

  • Full Member
  • ***
  • Posts: 219
Re: Progress bar in stringgrid
« Reply #13 on: July 17, 2021, 03:43:25 pm »
So I am basically using this code here which is pretty much what you all put together for me with a few small changes.  My current issue is this...(hard to explain but i'll try)... 
I process the list of files in my string grid.  I set the cell text to a string 'DONE'.  <-- works good, the custom drawn progressbar disapears in the cell.  Perfect

Now lets say I go through and process my list again... right away all of those cells show right away with what appears to be the last custom drawn that was in them.

Is the issue possibly in this custom cell drawing event?  Should I be resetting the custom drawing properties this event is setting some how?


Update:
As far as I can tell it just seems to me that each cell is holding the properties of the canvas drawing properties being set.  I did try things like stringgrid.canvas.clear but it doesn't seem to work... should I just make it copy the canvas properties from another cell when it is done with the progressbar drawing bit?  Or is there something simpler that I am missing?

Update again...  %)  I probably prematurley asked for help... I kind of know what my issue is... just dont know how to solve it.  I think I need to learn more about this canvas property and i'll be back with a more direct and proper question...  :-[

Code: Pascal  [Select][+][-]
  1. procedure TfrmMain.StringGridFMDrawCell(Sender: TObject; aCol, aRow: Integer;
  2.   aRect: TRect; aState: TGridDrawState);
  3. var
  4.   R: TRect;
  5.   Index: Integer;
  6. begin
  7.   if (doCustDrawSG) and (aCol = 9) and (aRow >= StringGridFM.FixedRows) then begin
  8.     Index := aRow - StringGridFM.FixedCols;
  9.     if PERCENTDONEsingle >= 1 then begin
  10.       StringGridFM.Canvas.Brush.Color := StringGridFM.Color;
  11.       StringGridFM.Canvas.FillRect(aRect);
  12.       StringGridFM.Canvas.Font.Color := clPurple;
  13.       //StringGridFM.Canvas.TextRect(aRect, aRect.Left + 2, aRect.Top + 2, '1111');
  14.       R := aRect;
  15.       R.Right := R.Left + Round(R.Width * PERCENTDONEsingle/100);
  16.       StringGridFM.Canvas.Brush.Color := $00EAA3DA;
  17.       StringGridFM.Canvas.FillRect(R);
  18.       StringGridFM.Canvas.TextRect(aRect, aRect.Left + 2, aRect.Top + 2, FormatFloat('0.00%',PERCENTDONEsingle));
  19.       //StringGridFM.Canvas.Font.Color := clWhite;
  20.       //StringGridFM.Canvas.TextRect(R, R.Left + 2, R.Top + 2, '222222');
  21.  
  22.       Exit;
  23.     end;
  24.   end;
  25.   StringGridFM.DefaultDrawCell(aCol, aRow,aRect,aState);
  26.  
  27.  // StringGridFM.Canvas.FillRect(aRect.Left,aRect.Top,aRect.Left + round(aRect.Width * PERCENTDONEsingle/100),aRect.Top+aRect.Height);
  28.  
  29.  
  30. end;  
  31.  
« Last Edit: July 17, 2021, 04:35:21 pm by Tony Stone »

Tony Stone

  • Full Member
  • ***
  • Posts: 219
Re: Progress bar in stringgrid
« Reply #14 on: July 17, 2021, 04:40:59 pm »
Ok... my more proper question...
So far my understanding is this...

In DrawCell event I am setting properties of canvas such as fillrect and text rect and brush color.

Now it seems those properties essentially 'stick' forever or until i change them again... or exit the application and start over...(obviously).  Is this a correct statement?

DefaultDrawCell doesn't get rid of or clear those canvas/brush/rect properties.... or should it?




 

TinyPortal © 2005-2018