Recent

Author Topic: Data structure in a boolean DrawGrid  (Read 930 times)

kupferstecher

  • Sr. Member
  • ****
  • Posts: 324
Data structure in a boolean DrawGrid
« on: November 16, 2019, 11:47:17 am »
Hello all,

I need a component like TStringGrid or TDrawGrid to display boolean data. I.e. each cell should display a rectangle or circle when the value is true and stay blank for false.

-> What component would be suitable for that and how to manage the data?


A StringGrid with overridden Paint method would be possible and the data could be written in the strings as ascii, e.g. as '0' and '1'. But I don't like the way of storing the data.

An other possibility would be to use a DrawGrid and implement a data structure for the boolean values or pass a reference to a data structure. An internal data structure would make the component more self contained, but would result either in a double storage of the data, once in the grid and once in the programm logic. Or it would make the programm logic too much bound to the Grid, if the grid internal data structure is used in the complete program.
The data structure could be passed to the grid as pointer or reference, but the the grid would become quite specific and this somehow results in decentral access to the data structure.

About the data structure itself, using an "array of array of Boolean" would be easy, but the dimension changes often, so this would afaik result in a lot of internal copying when the array bounds are increased. Also reading and writing data I think would result in a lot of copying and a class based data structure could be better, then only the references need to be passed.

-> How would You do that, what is a good way to have clean code and reduce entanglement?
In the application the grid will be the editor for manipulating the data, so there will be marking, moving, inserting etc.

Thanks in advance.

Regards~


Aidex

  • New Member
  • *
  • Posts: 28
Re: Data structure in a boolean DrawGrid
« Reply #1 on: November 16, 2019, 12:15:17 pm »
I would take a TStringGrid, but leave the strings empty.
Instead I would use the .Objects properties to store the values, e.g. StringGrid.Objects[x,y]:=nil for False and <>nil for True.

kupferstecher

  • Sr. Member
  • ****
  • Posts: 324
Re: Data structure in a boolean DrawGrid
« Reply #2 on: November 16, 2019, 01:02:23 pm »
Hello Aidex,

thanks, thats a good advice. I'll try.

zeljko

  • Hero Member
  • *****
  • Posts: 1089
    • http://wiki.lazarus.freepascal.org/User:Zeljan
Re: Data structure in a boolean DrawGrid
« Reply #3 on: November 16, 2019, 01:28:08 pm »
Try with TVirtualStringTree. It's much faster than TStringGrid and you can manipulate data in any way.

wp

  • Hero Member
  • *****
  • Posts: 6471
Re: Data structure in a boolean DrawGrid
« Reply #4 on: November 16, 2019, 03:47:02 pm »
My grid of choice for non-string data usually is TDrawGrid. Normally you only must provide the OnDrawCell event to get things running (editing is a bit more complex). Of course, you need the OnDrawCell event also for the string cells, not only for the boolean cells. But it requires only a few lines of code. In https://forum.lazarus.freepascal.org/index.php/topic,47074.msg339537.html#msg339537 I recently published a demo application based on a TDrawGrid.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

kupferstecher

  • Sr. Member
  • ****
  • Posts: 324
Re: Data structure in a boolean DrawGrid
« Reply #5 on: November 16, 2019, 05:48:19 pm »
@Zeljko:
Thanks for the hint. I have to take some time understanding the component first.

@wp:
Thanks for the explanation. Actually I don't worry about the drawing, as I just need rectangles. The cell focus probably is more difficult, but one thing after an other.
The data structure question, though,  is still unsolved. Does the draw grid as well provide cell data variables, e.g. for a pointer (I think not)?
Or I make a TDrawGrid derived class and place a TList inside for the datapoints, each containing a pointer to a fixed length array for each column. Perhaps contained in a simple class to avoid actual pointers.

winni

  • Hero Member
  • *****
  • Posts: 590
Re: Data structure in a boolean DrawGrid
« Reply #6 on: November 16, 2019, 06:01:54 pm »
Hi!

The DrawGrid  can ony show data, the StringGrid stores a string for every cell.

With the StringGrid you can just store a '0'or '1' in every cell. With the help of boolToStr you
can store a "True" or "False" in the grid. Or a "Ja" or " Nein".

If you change the dimensions of a StringGrid the data stay undestroyed in the cells - until you delete a row or a column.

But in this case you don't separate the data from the display. This gives bad notes. In school and from some coders.

As further step for a nice display you add an ImageList with 2 images for "True" and "False".
Then you switch to DefaultDrawing = false and write your own DrawCell procedure that shows one of the images for each cell.

Winni
« Last Edit: November 16, 2019, 06:04:49 pm by winni »

wp

  • Hero Member
  • *****
  • Posts: 6471
Re: Data structure in a boolean DrawGrid
« Reply #7 on: November 16, 2019, 06:07:32 pm »
TDrawGrid does not have any data. It just draws "something" into cells given by their column and row index. It's up to you to decide what "something" is and where it is stored. You must use the OnDrawCell to display that "something" in the cell to which it belongs.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

jamie

  • Hero Member
  • *****
  • Posts: 2162
Re: Data structure in a boolean DrawGrid
« Reply #8 on: November 16, 2019, 06:23:45 pm »
I use the TDrawGrid very often, it allows me to do all sorts of things.

In this case, one can simply read an array or some variable's bit fields to paint the cells in the manner you wish..

 A StringGrid will work too if you want to use the strings to store the values but that gets cumbersome with execution time and code bloat.

Number 1 at blue screen app creations!

wp

  • Hero Member
  • *****
  • Posts: 6471
Re: Data structure in a boolean DrawGrid
« Reply #9 on: November 16, 2019, 06:42:10 pm »
A StringGrid will work too if you want to use the strings to store the values but that gets cumbersome with execution time and code bloat.
Yes. People sometimes want to display millions of strings in a stringgrid and wonder why their programs becomes painfully slow. It is clear -- the grid must populate all the millions of cells with strings. Not so with the drawgrid -- it handles only the strings currently displayed.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

dpremus

  • New Member
  • *
  • Posts: 12
Re: Data structure in a boolean DrawGrid
« Reply #10 on: November 16, 2019, 07:16:26 pm »

You can inherit your custom grid from TDrawGrid If you want to keep data only in one place.

For example, your new grid will have some event like this:

procedure TBooleanGrid.OnNeedData(Sender: TObject; Index: Integer; Out Value: Boolean);
begin
   // passing data to the grid, the grid is virtual nothing will be stored inside a grid
   Value := MyData[Index];
end;

So drawing routines will be hidden inside your grid and your code that uses that grid will be clean.
« Last Edit: November 16, 2019, 07:19:05 pm by dpremus »

kupferstecher

  • Sr. Member
  • ****
  • Posts: 324
Re: Data structure in a boolean DrawGrid
« Reply #11 on: November 17, 2019, 01:09:33 pm »
But in this case you don't separate the data from the display. This gives bad notes. In school and from some coders.
Yes, thats also the reason I asked, how to make it the clean way.

I think for the data I go with a TList containing references to the column data. And then use TDrawGrid as suggested.

@dpremus: The callback is a good idea, I'll consider.

@All: Thanks for your efforts!

howardpc

  • Hero Member
  • *****
  • Posts: 3201
Re: Data structure in a boolean DrawGrid
« Reply #12 on: November 19, 2019, 11:27:49 pm »
Here is an example of a virtual grid-of-booleans custom control.

The grid contains no boolean data, this is fetched as needed, whether for cell display or for hints. For small datasets (such as can be shown on a typical screen) data changes  appear instantaneous.

The example calculates the date of Easter for a variable range of years.

kupferstecher

  • Sr. Member
  • ****
  • Posts: 324
Re: Data structure in a boolean DrawGrid
« Reply #13 on: November 20, 2019, 01:49:42 pm »
Hello howardpc,

thanks for the example. Although I have my own BoolGrid running by now, but some details help me to improve it, e.g. I wasn't aware of the TGridDrawState.

About the data structure I chose to make a class TBoolMatrix derived from TList, containing a Column class for each column. The class instance is created and handled in the main form, but assigned to the Grid in the beginning of the program. So the grid can access the data without callbacks but through the defined class interface.

The TList derived structure is a bit complicated, but easy to access (BoolMatrix1.Columns[1].Row[2]). And I hope that thanks to the TList it will perform well for increasing matrix sizes. A tricky thing is, if I access a delete feature of the TList directly, then there will be a memory leak.

By the way I found two good descriptions for the usage of grids:
https://wiki.freepascal.org/Grids_Reference_Page
https://www.freepascal.org/~michael/articles/grids/grids.pdf

Here's the interface part of my bool matrix:

Code: Pascal  [Select]
  1. Type TBoolColumn = class;
  2.  
  3. Type
  4.  
  5. { TBoolMatrix }
  6.  
  7.  TBoolMatrix = class(TList)
  8.   public
  9.     OnSizeChange: TNotifyEvent;
  10.   public
  11.     Procedure AddColumns(AQty: Integer= 1);
  12.     Procedure ExtendToCol(AColIndex: integer);
  13.     Procedure DeleteLast;
  14.     Procedure Clear;override;
  15.   private
  16.     function GetColCount: Integer;
  17.     function GetCol(Index: Integer): TBoolColumn;
  18.     procedure SetCol(Index: Integer; AValue: TBoolColumn);
  19.   public
  20.     Property Column[Index: Integer]: TBoolColumn read GetCol write SetCol;
  21.     Property ColCount: Integer read GetColCount;
  22.   private
  23.     Property Items;
  24. end;
  25.  
  26. Type
  27.  
  28. { TBoolColumn }
  29.  
  30.  TBoolColumn = class
  31.    private
  32.      fColumn: Array[1..27] of boolean;
  33.    private
  34.      function GetRow(Index: Integer): Boolean;
  35.      procedure SetRow(Index: Integer; AValue: Boolean);
  36.    public
  37.      property Row[Index: Integer]: Boolean read GetRow write SetRow; default;
  38.  end;        


wp

  • Hero Member
  • *****
  • Posts: 6471
Re: Data structure in a boolean DrawGrid
« Reply #14 on: November 20, 2019, 02:04:49 pm »
I guess you don't free the TBoolColumn. The TList does not do it because it stores only pointers and does not "know" how to do it. Since TBoolColumn is a class you should use TObjectList instead of TList - it stores objects and as they have a destructor Destroy the list is able to destroy the stored itemd. TObjectList is in unit cntnrs.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10