我在TDBChart上显示参加电影节目的人数,节目的日期是X轴,人的数量是Y轴。不同电影之间的分割由堆叠列表示。
我通过为每部电影创建一个条形系列(动态创建)来实现这一目标。但是当我显示两部以上电影的数据时,所有日期都不会显示在X轴上,只会添加第一个系列(电影)的日期。更进一步的系列只是被挤进了第一个系列的日期。
在这里,您只能看到幽灵公主的图表,日期是正确的。 日期范围选择器目前不执行任何操作。
但是当你把它们放在一起时Reapers的数据被扔进了Mononokes公主的日期,尽管Reaper并没有出现在幽灵公主的日期。和收割者的日期完全省略了??
这是添加系列(电影)的代码,我还为该系列动态创建SQL查询。 qryStatMovieSelection就是所有选中的电影。 我真的不知道如何处理这个,任何想法?
DataModule1.qryStatMovieSelection.First;
for iCount := 0 to iSelectedMovies - 1 do
begin
arrQryDateCustomers[iCount] := TADOQuery.Create(Self);
With arrQryDateCustomers[iCount] do
begin
Connection := conCinema;
SQL.Clear;
SQL.Add(
'SELECT Movies.MovieID, Name, ShowDate,(SUM(NormalTickets) + SUM(ChildTickets) + SUM(SeniorTickets)) AS SeatsBooked');
SQL.Add('FROM Movies, Invoice, Shows');
SQL.Add('WHERE Invoice.ShowID = Shows.ShowID AND Shows.MovieID = Movies.MovieID');
// MovieID in selected table
SQL.Add('AND Movies.MovieID = '+ DataModule1.qryStatMovieSelection.FieldByName('MovieID').AsString);
SQL.Add('GROUP BY Movies.MovieID, Name, ShowDate');
Open;
end;
//create series
arrBarSeriesMovies[iCount] := TBarSeries.Create(Self);
With arrBarSeriesMovies[iCount] do
begin
ParentChart := crtStatistics;
YValues.ValueSource := 'SeatsBooked';
XValues.DateTime := True;
XLabelsSource := 'ShowDate';
DataSource := arrQryDateCustomers[iCount];
MultiBar := mbStacked;
Marks.Style := smsValue;
Marks.ArrowLength := -20;
Color := arrColor[iCount];
Title := DataModule1.qryStatMovieSelection.FieldByName('Name').AsString;
Active := True;
end;
DataModule1.qryStatMovieSelection.Next;
end;
end
答案 0 :(得分:0)
我知道你想要堆叠具有相同日期的条形码,即使它们在时间上不一致。
问题是TBarSeries
会堆叠ValueIndex
中重合的条形,而不是XValue
。因此,您应该在系列中添加一个Null点,该系列没有售出日期的票证,以便在两个系列中具有相同的点数。
首先,我按如下方式初始化数据:
type TShow = class
ShowDate: TDateTime;
TotalTickets: Integer;
end;
type TMovie = class
Title: String;
Shows: array of TShow;
end;
procedure TForm1.FormCreate(Sender: TObject);
var movies: array of TMovie;
begin
SetLength(movies, 2);
movies[0]:=TMovie.Create;
movies[1]:=TMovie.Create;
with movies[0] do
begin
Title:='Princess Mononoke';
SetLength(Shows, 2);
Shows[0]:=TShow.Create;
Shows[1]:=TShow.Create;
Shows[0].ShowDate:=StrToDateTime('14/07/2017 15:00');
Shows[0].TotalTickets:=19;
Shows[1].ShowDate:=StrToDateTime('20/07/2017 17:30');
Shows[1].TotalTickets:=5;
end;
with movies[1] do
begin
Title:='Reaper';
SetLength(Shows, 2);
Shows[0]:=TShow.Create;
Shows[1]:=TShow.Create;
Shows[0].ShowDate:=StrToDateTime('20/07/2017 15:30');
Shows[0].TotalTickets:=1;
Shows[1].ShowDate:=StrToDateTime('23/07/2017 17:30');
Shows[1].TotalTickets:=15;
end;
end;
然后我用这个函数计算给定电影的最小和最大日期:
procedure TForm1.FormCreate(Sender: TObject);
procedure MinMaxDates(AMovies: array of TMovie; var MinDate: TDateTime; var MaxDate: TDateTime);
var i, j: Integer;
begin
MinDate:=Trunc(AMovies[0].Shows[0].ShowDate);
MaxDate:=Trunc(AMovies[0].Shows[0].ShowDate);
for i:=0 to High(AMovies) do
for j:=0 to High(AMovies[i].Shows) do
begin
MinDate:=Min(MinDate, Trunc(AMovies[i].Shows[j].ShowDate));
MaxDate:=Max(MinDate, Trunc(AMovies[i].Shows[j].ShowDate));
end;
end;
var movies: array of TMovie;
minDate, maxDate: TDateTime;
begin
// Data initialization here
MinMaxDates(movies, minDate, maxDate);
现在我可以在这里考虑几个选项。
选项1 :即使没有售出门票,您也可以在minDate
和maxDate
之间的所有日期添加所有系列中的积分。此选项尊重条之间距离的比例,并给出细条:
procedure TForm1.FormCreate(Sender: TObject);
function TicketsAtDate(AMovie: TMovie; ADate: TDateTime): Integer;
var i: Integer;
begin
Result:=0;
for i:=0 to High(AMovie.Shows) do
if Trunc(AMovie.Shows[i].ShowDate) = ADate then
begin
Result:=AMovie.Shows[i].TotalTickets;
Break;
end;
end;
var i, j: Integer;
movies: array of TMovie;
minDate, maxDate: TDateTime;
begin
// Data initialization here
MinMaxDates(movies, minDate, maxDate);
for i:=0 to High(movies) do
begin
//create series
With Chart1.AddSeries(TBarSeries) as TBarSeries do
begin
XValues.DateTime := True;
MultiBar := mbStacked;
Marks.Style := smsValue;
Marks.ArrowLength := -20;
Title:=movies[i].Title;
j:=0;
while (minDate+j<=maxDate) do
begin
AddXY(minDate+j, TicketsAtDate(movies[i], minDate+j));
if YValue[Count-1] = 0 then
SetNull(Count-1);
Inc(j);
end;
end;
end;
end;
选项2 :只有在每个日期都有售票的情况下才能添加积分。此选项仍然尊重条之间距离的比例,并给出宽条:
procedure TForm1.FormCreate(Sender: TObject);
function AnyTicketSold(AMovies: array of TMovie; ADate: TDateTime): Boolean;
var i, j: Integer;
begin
Result:=False;
for i:=0 to High(AMovies) do
for j:=0 to High(AMovies[i].Shows) do
if (Trunc(AMovies[i].Shows[j].ShowDate) = ADate) and (AMovies[i].Shows[j].TotalTickets > 0) then
begin
Result:=True;
Exit;
end;
end;
var i, j: Integer;
movies: array of TMovie;
minDate, maxDate: TDateTime;
begin
// Data initialization here
MinMaxDates(movies, minDate, maxDate);
for i:=0 to High(movies) do
begin
//create series
With Chart1.AddSeries(TBarSeries) as TBarSeries do
begin
XValues.DateTime := True;
MultiBar := mbStacked;
Marks.Style := smsValue;
Marks.ArrowLength := -20;
Title:=movies[i].Title;
j:=0;
while (minDate+j<=maxDate) do
begin
if anyTicketSold(movies, minDate+j) then
begin
AddXY(minDate+j, TicketsAtDate(movies[i], minDate+j));
if YValue[Count-1] = 0 then
SetNull(Count-1);
end;
Inc(j);
end;
end;
end;
end;
答案 1 :(得分:0)
好的,所以我找到了一种方法来使用临时表来插入空值作为间隔;就像Yeray的回答一样,只有临时表。如果有比我更多的知识的人可以比较这两者,我将不胜感激。
所以我首先用Y轴初始化日期和所有0值的X轴(在某些情况下只是为了避免0值作为日期);
With qryStatDateInitialization do
begin
SQL.Clear;
SQL.Add('SELECT DISTINCT ShowDate, 0 AS Blank');
SQL.Add('FROM Shows');
SQL.Add('WHERE MovieID IN (' + MovieIDs + ')');
Open;
end;
SetLength(arrDates, 0);
//Populate dates array
qryStatDateInitialization.First;
SetLength(arrDates, qryStatDateInitialization.RecordCount);
for iCount := 0 to qryStatDateInitialization.RecordCount - 1 do
begin
arrDates[iCount] := qryStatDateInitialization.FieldByName('ShowDate').AsString;
qryStatDateInitialization.Next;
end;
//Series to initialize dates on X-axis
barSeries := TBarSeries.Create(Self);
barSeries.ShowInLegend := False;
barSeries.ParentChart := crtStatistics;
barSeries.DataSource := qryStatDateInitialization;
barSeries.YValues.ValueSource := 'Blank';
barSeries.XLabelsSource := 'ShowDate';
barSeries.Marks.Style := smsValue;
barSeries.MultiBar := mbStacked;
barSeries.Marks.Hide;
barSeries.ColorEachPoint := True;
barSeries.Active := True;
接下来,我创建临时表(对于每个电影),然后在适当的位置插入0值记录。我使用ID字段。
for iCount := 0 to iSelectedMovies - 1 do
begin
//Create table for movie
With qryEdit do
begin
//Create show table with autonumer
SQL.Clear;
SQL.Add('CREATE TABLE tempTShow'+IntToStr(iCount));
SQL.Add('(ID COUNTER , Name string, ShowDate string, SeatsBooked string)');
ExecSQL;
//Add data to show table
SQL.Clear;
SQL.Add('INSERT INTO tempTShow'+IntToStr(iCount));
SQL.Add('SELECT Name, ShowDate, SeatsBooked');
SQL.Add('FROM (SELECT Name, ShowDate,(SUM(NormalTickets) + SUM(ChildTickets) + SUM(SeniorTickets)) AS SeatsBooked');
SQL.Add('FROM Movies, Invoice, Shows');
SQL.Add('WHERE Invoice.ShowID = Shows.ShowID AND Shows.MovieID = Movies.MovieID');
SQL.Add('AND Movies.MovieID = ' + DataModule1.qryStatMovieSelection.FieldByName('MovieID').AsString);
SQL.Add('GROUP BY Name, ShowDate');
SQL.Add('ORDER BY ShowDate ASC)');
ExecSQL;
//Set autonumber to normal integer to set show positions accordingly
SQL.Clear;
SQL.Add('ALTER TABLE tempTShow'+IntToStr(iCount));
SQL.Add('ALTER COLUMN ID integer');
ExecSQL;
Inc(iTempTableCountInternal);
end;
qryStats.SQL.Text := 'SELECT * FROM tempTShow'+IntToStr(iCount);
qryStats.Open;
//Insert 0 value spacers in temp table
iCountDateSort := 0;
iDateOn := 0;
While iDateOn < length(arrDates) - 1 do
begin
sDate := qryStats.FieldByName('ShowDate').AsString;
if NOT (sDate = arrDates[iDateOn]) then
With qryEdit do
begin
SQL.Clear;
SQL.Add('UPDATE tempTShow'+IntToStr(iCount));
SQL.Add('SET ID = (ID+1) WHERE ID >= ' + IntToStr(iCountDateSort+1));
ExecSQL;
SQL.Clear;
SQL.Add('INSERT INTO tempTShow'+IntToStr(iCount));
SQL.Add('(ID, Name, ShowDate, SeatsBooked) VALUES ('
+IntToStr(iCountDateSort+1) + ',"0","0",0)' );
ExecSQL;
end
else
begin
qryStats.Next;
Inc(iCountDateSort);
//iDateOn := iCountDateSort;
end;
Inc(iDateOn);
end;
然后使用临时表作为数据源创建系列,并将0值设置为null
//Create query for series
arrQrys[iCount] := TADOQuery.Create(Self);
arrQrys[iCount].Connection := conCinema;
arrQrys[iCount].SQL.Text := 'SELECT * FROM tempTShow'+IntToStr(iCount)+' ORDER BY ID';
arrQrys[iCount].Open;
// create series
arrBarSeries[iCount] := TBarSeries.Create(Self);
With arrBarSeries[iCount] do
begin
ParentChart := crtStatistics;
YValues.ValueSource := 'SeatsBooked';
arrBarSeries[iCount].
XValues.DateTime := True;
XLabelsSource := 'ShowDate';
DataSource := arrQrys[iCount];
MultiBar := mbStacked;
Marks.Style := smsValue;
Marks.ArrowLength := -20;
Color := arrColor[iCount];
Title := DataModule1.qryStatMovieSelection.FieldByName('Name')
.AsString;
Active := True;
for i := 0 to Count - 1 do
begin
if YValue[i-1] = 0 then
SetNull(i-1);
end;
end;