I have a dataset which returns data like below
Date, Area, Sales
2017-06-01 00:00:00.000 Canteen 435.29
2017-06-01 00:00:00.000 Gym 26
2017-06-01 00:00:00.000 Nails 75
2017-06-01 00:00:00.000 Uncategorized 482.5
I am trying to create a stacked bar chart using this data
The number of series will be different
I have the logic below to create the series, but I am not sure how to add each value, e.g. I need the X Axis to be by date and have 2 bars. Each bar is then stacked with 3 series, as per the above data.
while not tblSalesBreakdownByDate.Eof do
begin
nIndex := objList.IndexOf(tblSalesBreakdownByDateCategory.AsString);
if nIndex = -1 then
begin
objSeries := TBarSeries.Create(Self);
objSeries.MultiBar := TMultiBar.mbStacked;
objSeries.Title := tblSalesBreakdownByDateCategory.AsString;
chrtBreakdownByDate.AddSeries(objSeries);
objList.AddObject(objSeries.Title, objSeries)
end
else
objSeries := objList.Objects[nIndex];
objSeries.Add(tblSalesBreakdownByDateTotalSales.AsFloat, tblSalesBreakdownByDateTransactionDate.AsString);
tblSalesBreakdownByDate.Next;
end;
When this is rendered, instead of 1 bar which is stacked, I get 3 bars
How do I get this in 1 bar stacked for the date?
Is there something special I need to with the Axis?
Cheers
Paul
This gives me one single stacked column:
uses Series;
procedure TForm1.FormCreate(Sender: TObject);
begin
with Chart1.AddSeries(TBarSeries) as TBarSeries do
begin
MultiBar:=mbStacked;
Marks.Hide;
Title:='Canteen';
Add(435, '2017-06-01');
end;
with Chart1.AddSeries(TBarSeries) as TBarSeries do
begin
MultiBar:=mbStacked;
Marks.Hide;
Title:='Gym';
Add(25, '2017-06-01');
end;
with Chart1.AddSeries(TBarSeries) as TBarSeries do
begin
MultiBar:=mbStacked;
Marks.Hide;
Title:='Nails';
Add(95, '2017-06-01');
end;
with Chart1.AddSeries(TBarSeries) as TBarSeries do
begin
MultiBar:=mbStacked;
Marks.Hide;
Title:='Uncategorized';
Add(455, '2017-06-01');
end;
end;
Related
I want to create a different Popup for each column. Since the columns order can be changed I need to identify that by the column title but I have not found a solution.
These are two method I have applied without success.
procedure TForm2.DBGrid1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
ACol, ARow: Integer;
begin
if Y < DBGrid1.DefaultRowHeight then
begin
(Sender as TDBGrid).MouseToCell(X, Y, ACol, ARow);
if Button = mbRight then
begin
if DBGrid1.SelectedColumn.FieldName = 'Title1' then
BEGIN
ShowMessage('Title1'+ IntToStr(ACol));
end;
if DBGrid1.SelectedColumn.FieldName = 'Title2' then
BEGIN
ShowMessage('Title2'+ IntToStr(ACol));
end;
end;
end;
end;
It is not functional because it identifies the columns by id and not the name so if the user change the columns order it will not work fine.
Also this faily code
procedure TForm2.Button2Click(Sender: TObject);
var
i: Integer;
CaptionText: string;
begin
for i := 0 to DBGrid1.Columns.Count - 1 do
case DBGrid1.Columns[i].FieldName of
'TEST':
begin
DBGrid1.Columns[i].Title.Caption := 'REPLACE TEXT';
end;
end;
end;
It simply replace all titles in a click.
What I'm looking to do is to create an IF like this that handle on right clicking column title:
if selected column name = 'test' then
begin
showmessage('You have selected test column');
end;
I'll use the if to create dynamic popup to apply filters.
The code below is an event handler for a TDBGrid's MouseMove event which
displays the Caption of the Title of the column the mouse is over on the caption of the form.
procedure TForm1.DBGrid1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
Col,
Row : Integer;
begin
Col := DBGrid1.MouseCoord(X, Y).X;
Row := DBGrid1.MouseCoord(X, Y).Y;
if (dgIndicator in DBGrid1.Options) then
Dec(Col);
if (Col >= 0) and (Col < DBGrid1.Columns.Count) then
Caption := DBGrid1.Columns[Col].Title.Caption
else
Caption := '';
end;
Note that the
if (dgIndicator in DBGrid1.Options) then
Dec(Col);
is to adjust the behaviour for correct operation if the dgIndicator option is turned off.
Obviously, instead of
Caption := DBGrid1.Columns[Col].Title.Caption
you could do
MenuItem.Caption := DBGrid1.Columns[Col].Title.Caption
to copy the column title to a menuitem's caption. I think you probably don't need to do any comparison of the Title.Caption's value with any hard-coded constants, but obviously that's your choice.
Btw, if you would rather access the name of the dataset field which is supplying the displayed values for the Column's contents, you can read the Column's FieldName property.
I am trying to show data from "CSV" File to Oracle forms. I am using Client_Text_IO procedure to read data from "CSV" file with comma separated into Data Block.
I have one column in which some data empty and have some data. If I open file in excel then:
No. 6 column some rows are empty:
No.6 column some rows have data:
When I open file in notepad then:
6,6,6000000116,HH00000471,Abdul akbar,,1610223056753
You can see after "ABDUL AKBAR" there are 2 commas due to empty data
With data in notepad:
6,6,6000000189,HH00000544,Raishma bibi,Gul akbar,1610216789294
When I run procedure then I getting following error on 6th column:
CODE:
PROCEDURE p_output_line(p_line varchar2) IS
vLINE VARCHAR2(4000);
vVALUE VARCHAR2(1000);
vCOMMA_COUNT NUMBER;
BEGIN
vLINE := p_line;
vCOMMA_COUNT := LENGTH(vLINE)- LENGTH(REPLACE(vLINE,',',''));
FOR I IN 1.. vCOMMA_COUNT+1 LOOP
vVALUE := SUBSTR(vLINE,1,INSTR(vLINE,',')-1);
IF vVALUE IS NULL THEN
vVALUE := vLINE;
END IF;
vLINE := SUBSTR(vLINE,INSTR(vLINE,',')+1) ; -- CHANGE 123,ABC,9877 TO BE ABC,9877
IF I = 1 THEN
:WE_GROUP_HOF_K.CLIENTID := vVALUE;
END IF;
IF I = 2 THEN
:WE_GROUP_HOF_K.PROJECTID := vVALUE;
END IF;
IF I = 3 THEN
:WE_GROUP_HOF_K.GROUP_HOF_ID := vVALUE;
END IF;
IF I = 4 THEN
:WE_GROUP_HOF_K.NRSP_HOFID := vVALUE;
END IF;
IF I = 5 THEN
:WE_GROUP_HOF_K.HOF_NAME := vVALUE;
END IF;
IF I = 6 THEN
:WE_GROUP_HOF_K.FATHER_NAME := vVALUE;
END IF;
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
MESSAGE('Please Check the data type is appropriate on you excel file');
MESSAGE('Please Check the data type is appropriate on you excel file');
END;
Because it is empty your counts don't work.
Use a split function to split your data.
Split function
Also in your when no_data_found exception you should put pause; after your message, then there is no need to put it twice.
If I have a logarithmic x-axis with value from 10 to 400 it displays 10 and 100, but I also want it to display the end value 400.
Another problem is if the values goes from 11 to 400, it only displays label at 100. Here I want to display 11, 100 and 400.
Anyone know what axis/label property to set for this?
To draw such labels you should use custom labels.
I'll assume you are using TeeChart VCL and I'll show code in Delphi, but it would be similar with TeeChart .NET, TeeChart ActiveX or TeeChart Java.
If I have a logarithmic x-axis with value from 10 to 400 it displays 10 and 100, but I also want it to display the end value 400.
You could do it as follows:
uses Series, Math;
procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
begin
Chart1.View3D:=False;
Chart1.Legend.Hide;
Chart1.Walls.Hide;
Chart1.Gradient.Visible:=False;
Chart1.Color:=clWhite;
Chart1.Axes.Bottom.Logarithmic:=True;
with Chart1.AddSeries(TFastLineSeries) as TFastLineSeries do
begin
for i:=10 to 400 do
AddXY(i, sin(i/100));
end;
with Chart1.Axes.Bottom.Items do
begin
Clear;
Add(10, '10');
Add(100, '100');
Add(400, '400');
end;
end;
Another problem is if the values goes from 11 to 400, it only displays label at 100. Here I want to display 11, 100 and 400.
You could do it as follows:
uses Series, Math;
procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
begin
Chart1.View3D:=False;
Chart1.Legend.Hide;
Chart1.Walls.Hide;
Chart1.Gradient.Visible:=False;
Chart1.Color:=clWhite;
Chart1.Axes.Bottom.Logarithmic:=True;
with Chart1.AddSeries(TFastLineSeries) as TFastLineSeries do
begin
for i:=11 to 400 do
AddXY(i, sin(i/100));
end;
with Chart1.Axes.Bottom.Items do
begin
Clear;
Add(11, '11');
Add(100, '100');
Add(400, '400');
end;
end;
I've got a project at schools which requires to write datas from a .txt file to a "memobox" in Lazarus freepascal.
There are datas in order like this.
Budapest tomato 23
Dublin tv 45
Rosslare projector 43
etc.
I have to read these datas from a .txt file and then write them into a memobox in Lazarus freepascal.
If I am not mistaken I have already copyed the datas from the .txt file but I have no idea how to write them.
I've already written this code:
type
cityname:integer;
product:string;
quantity:integer;
var
Form1: TForm1;
ceg:array[1..5] of rektip;
db:integer;
implementation
procedure TForm1.Button1Click(Sender: TObject);
var f:textfile; i:integer; a:char;
begin
assignfile(f,'termek.txt');
reset(f);
readln(f,db);
While not eof(f) do
begin
readln(f,ceg[i].varosnev,a,ceg[i].termek,a,ceg[i].darabszam);
end;
db:=1;
closefile(f);
end;
procedure TForm1.Button2Click(Sender: TObject);
var i:integer;
begin
For i:=1 to db do
Memo1.lines.add(ceg[i].varosnev,ceg[i].termek,IntToStr(ceg[i].darabszam));
end;
end;
I would like to know how to fix it.
The code you posted is incomplete, so I assume:
type
rektip = record
varosnev: string;
termek: string;
darabszam: Integer;
end;
There is a lot wrong with your approach:
readln(f,db);
While not eof(f) do
begin
readln(f,ceg[i].varosnev,a,ceg[i].termek,a,ceg[i].darabszam);
end;
db:=1;
closefile(f);
end;
You are not initializing nor updating i, so you are reading all data into the same record, and you don't even know which one (i could be 100 and you'd be writing the data to somewhere unknown -- there is no ceg[100]). That results in undefined behaviour (and that can be a crash too).
Do something like:
var
ceg: array of rektip;
...
begin
AssignFile(f, 'termek.txt');
Reset(f);
Readln(f, db);
if db = 0 then
Exit;
SetLength(ceg, db);
i := 0;
while not eof(f) do
begin
Readln(f, ceg[i].varosnev, ceg[i].termek, ceg[i].darabszam);
Inc(i);
if i > High(ceg) then
Break;
end;
SetLength(ceg, i); // remove any empty slots.
CloseFile(f);
end;
Now you can put them into the TMemo:
for i := Low(ceg) to High(ceg) do
begin
Memo1.Lines.Add(Format('%s %s %d', [ceg[i].varosnev, ceg[i].termek, ceg[i].darabszam]));
end;
Note that the code above, reading from the file, assumes the file looks like:
3
Budapest tomato 23
Dublin tv 45
Rosslare projector 43
i.e. each "record" is on a line of its own and the first line contains the number of records.
So my code only reads one set of albums from my textfile and refused to read the rest in the file. For some reason why I get it to read album, it only displays that the file contain "1" album instead of "2".Even though in my loop i've SetLength the array to equal the albumNumber so the array size should be set to that when i begin my for loop.
textfile
2 (how many albums)
Adele (name)
Pop (genre)
2 (tracks)
Hello (Track 1)
ff (location)
Remedy (track 2)
dd (location)
Jcole
Rap
2
Toto
ff
Africa
dd
pascal
type
TrackRec = record
name: String;
location: String;
end;
GenreType = (Pop, Rap, Rock, Classic);
AlbumRec = Record
name: String;
genre: GenreType;
tracks: array of TrackRec;
end;
type AlbumArray = array of AlbumRec;
procedure ReadAlbum(var albums: AlbumArray; var myFile: TextFile);
var
albumNumber, tracknumber, count, i: Integer;
begin
AssignFile(myFile, 'mytestfile.dat');
Reset(myFile);
ReadLn(myFile, albumNumber);
WriteLn('This file contained ', albumNumber, ' album/s');
SetLength(albums, albumNumber);
for i := 0 to High(albums) do
begin
ReadLn(myFile, albums[i].name);
ReadLn(myFile, albums[i].genre);
ReadLn(myFile, tracknumber);
SetLength(albums[i].tracks, tracknumber);
for count := Low(albums[count].tracks) to tracknumber - 1 do
begin
ReadLn(myFile, albums[i].tracks[count].name);
ReadLn(myFile, albums[i].tracks[count].location);
end;
end;
end;
procedure Main();
var
i, count, select, change: Integer;
albums: AlbumArray;
myFile: TextFile;
message: String;
begin
WriteLn('Please select an option: ');
WriteLn('-------------------------');
WriteLn('1. Read Filename');
WriteLn('2. Display Albums');
WriteLn('3. Select an Album');
WriteLn('4. Update an Album');
WriteLn('5. Exit');
WriteLn('-------------------------');
repeat
i := ReadInteger('Select option for menu:');
case i of
1: ReadAlbum(albums, myFile);
2: PrintAll(albums, myFile);
3: PlayAlbum(albums, myFile);
4: Update(albums, myFile);
end;
until i = 5;
end;
Your code includes a number of routines (e.g. PlayAlbum) for which you
have not included the source.
Anyway, you may be relieved to know that in fact your ReadAlbum procedure does
work correctly, but perhaps you have gotten into a muddle thinking that it does not.
Down below, I've explained how you could have debugged it
to verify that it works. For now, replace your ReadAlbum and Main by the code below,
which also includes a couple of procedures to display the results.
Once you've done that, compile and run the app.
Code:
procedure ReadAlbum(var albums: AlbumArray; var myFile: TextFile);
var
albumNumber, tracknumber, count, i: Integer;
begin
AssignFile(myFile, 'D:\Delphi\Code\Lazarus\mytestfile.dat');
Reset(myFile);
ReadLn(myFile, albumNumber);
WriteLn('This file contained ', albumNumber, ' album/s');
WriteLn; // To space out the display
SetLength(albums, albumNumber);
for i := 0 to High(albums) do
begin
ReadLn(myFile, albums[i].name);
ReadLn(myFile, albums[i].genre);
ReadLn(myFile, tracknumber);
SetLength(albums[i].tracks, tracknumber);
for count := Low(albums[count].tracks) to tracknumber - 1 do
begin
ReadLn(myFile, albums[i].tracks[count].name);
ReadLn(myFile, albums[i].tracks[count].location);
end;
end;
end;
procedure PrintAlbum(Albums : AlbumArray; AlbumNumber : Integer);
var
Tracks : Integer;
Album : AlbumRec;
begin
// Note : This is incomplete, you should complete it yourself!
// I've used the local variable Album to avoid having to keep typing
// Albums[AlbumNumber] and because it makes inspection during debugging easier
Album := Albums[AlbumNumber];
WriteLn('Album number: ', AlbumNumber);
Writeln('AlbumName: ', Album.Name);
Writeln('AlbumGenre: ', Album.Genre);
Writeln; // to Space out the display;
end;
procedure PrintAlbums(Albums : AlbumArray);
var
AlbumNumber : Integer;
Album : AlbumRec;
begin
for AlbumNumber := 0 to High(Albums) do begin
Album := Albums[AlbumNumber];
PrintAlbum(Albums, AlbumNumber);
end;
end;
procedure Main();
var
albums: AlbumArray;
myFile: TextFile;
begin
ReadAlbum(Albums, MyFile);
PrintAlbums(Albums);
WriteLn('Done');
ReadLn;
end;
begin
Main;
end.
The following explains how to start debugging in the freeware Lazarus
IDE for FPC.
1 With your project open in the IDE, set a debugger breakpoint on the line
for i := 0 to High(albums) do
by either pressing F5 or clicking the blue circle in the "gutter"
on the left of the editor window. Either way, the line should turn red.
2 Compile and run the application. The black console window should appear
and then the debugger will stop on the BP set in step 1.
3 Press Ctrl-F5. This will pop op a Watch Properties dialog.
A watch is a way to get the debugger to display the value of a variable
as the program executes. Enter
Albums
in the Expression box.
If necessary, drag the Watch List window so that it doesn't overlap the code
editor window or the console window.
4 Now, repeatedly press F8 and observe the Watch List window carefully. F8 causes the debugger to execute the program a line at a time, called "single-stepping" for
obvious reasons. Note that the Watch List window already "knows" that there are 2 Album records and what there fields are.
As you single step through the for i:= and for count :=, notice how the fields
of the two records are progressively filled out.
Btw, Count is not a good name for a variable which actually rep
resents the TrackNumber
5 Eventually, the for i:= loop will finish, and at the point you know that the Albums array is correctly set up, including the second Album record.
6 Once you're a bit more familiar with the debugger, you could delete the BP you set in step 1. Instead, place a BP on the final line of ReadAlbums. After compiling and running the application, it will stop on the BP so that you can verify Albums's content without having to single-step each line of the two for loops.
7 Now, complete the coding of PrintAlbum. If necessary you can debug it as per steps 1-6
8 Search for online Lazarus debugging tutorials and read them until it gets boring.
Hopefully, by the time you've done all this, you'll have a better idea what information (including code) readers need to have in order to help. The key to
getting good help is to enable the reader to reproduce the problem, and your q doesn't do that. Readers shouldn't have to guess what missing code is supposed to do. Even if it is obvious, the problem may actually turn out to be somewhere in the code you haven't included, in which case readers can't help anyway.