TDBChart错误/省略的X轴值

时间:2017-07-30 16:52:03

标签: date delphi teechart

我在TDBChart上显示参加电影节目的人数,节目的日期是X轴,人的数量是Y轴。不同电影之间的分割由堆叠列表示。

我通过为每部电影创建一个条形系列(动态创建)来实现这一目标。但是当我显示两部以上电影的数据时,所有日期都不会显示在X轴上,只会添加第一个系列(电影)的日期。更进一步的系列只是被挤进了第一个系列的日期。

在这里,您只能看到幽灵公主的图表,日期是正确的。                                                                               日期范围选择器目前不执行任何操作。

这只是Reaper的图表,日期是正确的 enter image description here

但是当你把它们放在一起时Reapers的数据被扔进了Mononokes公主的日期,尽管Reaper并没有出现在幽灵公主的日期。和收割者的日期完全省略了?? enter image description here

这是添加系列(电影)的代码,我还为该系列动态创建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

2 个答案:

答案 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 :即使没有售出门票,您也可以在minDatemaxDate之间的所有日期添加所有系列中的积分。此选项尊重条之间距离的比例,并给出细条:

Option 1

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 :只有在每个日期都有售票的情况下才能添加积分。此选项仍然尊重条之间距离的比例,并给出宽条:

Option 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;