Technical Article

HotXLS diagramos, vaizdai ir grafiniai objektai „Delphi“ programoje

Viskas, kas plūduriuoja virš darbalapio tinklelio (diagrama, logotipas, antspaudas ar paaiškinimo laukelis), yra grafinis objektas (angl. drawing object), kurį apibrėžia du dalykai: kas tai yra ir kur jis yra prikabintas (angl. anchored). Prikabinimas (inkaras) yra ta dalis, kurioje kūrėjai dažniausiai klysta. Diagrama negyvena langelyje; ji patalpinta stačiakampyje, susietame su tam tikru eilučių ir stulpelių diapazonu, o duomenys, kuriuos ji atvaizduoja, yra atskiras „A1“ tipo nuorodų rinkinys, apie kurį inkaras nieko nežino. Pajudinkite rėmelį, ir grafikas liks savo vietoje. Įterpkite eilutes po juo, ir rėmelis pasislinks žemyn kartu su jomis. Šių dviejų koordinačių sistemų suderinimas yra pagrindinis uždavinys kuriant grafinį kodą.

„HotXLS“ yra vietinė (angl. native) „Object Pascal“ biblioteka, kuri skaito ir rašo XLS bei XLSX failus be „Excel“ automatizavimo (nenaudojant COM objektų). Ji palaiko du atskirus grafikos modelius, nes šie du failų formatai saugo brėžinius skirtingiems tikslams skirtingai. Senasis BIFF8 „.xls“ formatas laiko diagramas atskiruose tam skirtuose lapuose (angl. chart sheets), o plaukiojančias figūras įrašo į „OfficeArt“ srautą, pridedamą prie darbalapio. Naujasis OOXML „.xlsx“ formatas leidžia įterpti diagramą tiesiai į tinklelį, susiejant ją su langelio stačiakampiu, kartu su plaukiojančiais paveikslėliais ir figūromis. Objektų modelis atspindi šį pasidalijimą, o visos problemos kyla bandant vieno formato taisykles pritaikyti kitam.

Kurie konteineriai ką gali talpinti

Konteinerio pasirinkimas turi būti atliktas prieš pradedant rašyti bet kokį diagramos kodą, nes galimi objektų tipai skiriasi:

  • XLS (BIFF8): diagramos talpinamos atskiruose diagramų lapuose, sukurtuose per AddChartSheet metodą iš Sheets kolekcijos. Paveikslėliai, teksto laukeliai, stačiakampiai, ovalai ir linijos yra „OfficeArt“ figūros, valdomos per darbalapio Shapes kolekciją. Nėra galimybės įterpti diagramos į įprastą darbalapio tinklelį.
  • XLSX (OOXML): diagramos gali būti įterptos tiesiai į darbalapį su TXLSXWorksheet.AddChart, susiejant jas su langelių stačiakampiu, arba patalpintos atskirame diagramos lape su TXLSXWorkbook.AddChartSheet. Vaizdai pridedami su AddImage arba AddImageFromFile, o plaukiojantys teksto laukeliai – su AddTextBox.

Todėl reikalavimas „prietaisų skydelio lapas su diagrama šalia skaičių“ iš tikrųjų yra reikalavimas XLSX formatui. XLS formate tai galite tik apytiksliai imituoti nukreipdami diagramą į atskirą lapą, o tai keičia vartotojo navigaciją ir jūsų kodo struktūrą. Lapas, kurį grąžina XLS pusės AddChartSheet, yra diagramos srautas, o ne langelių tinklelis. Bandymas įrašyti į jį duomenis per Cells.Item sukuria sugadintą grafikos srautą, kuris bus sugeneruotas be klaidų, tačiau vėliau „Excel“ atidarant failą jį tiesiog atmes. Diagrama tiesiog išnyks, o kūrimo žurnale nebus jokių klaidų pranešimų. Traktuokite grąžintą lapą tik kaip diagramos konteinerį, ir išvengsite „dingusios diagramos“ problemų.

Diagramos įterpimas į XLSX darbalapį

XLSX kelias suteikia daugiausiai laisvės, ir būtent čia tampa svarbios dvi pradžioje minėtos koordinačių sistemos. Inkarinis stačiakampis, perduodamas funkcijai AddChart, nurodomas darbalapio eilutėmis ir stulpeliais bei fiksuoja diagramos rėmelio vietą. Duomenų sekos nurodomos kaip absoliučios „A1“ nuorodos, įskaitant lapo pavadinimą. Jos yra nepriklausomos: galite perkelti rėmelį į kitą lapo pusę, tačiau jame vis tiek bus atvaizduojami tie patys langeliai.

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Chart: TXLSXChart;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Sales');
    Sheet.Cells[1, 1].Value := 'Region';
    Sheet.Cells[1, 2].Value := 'Revenue';
    Sheet.Cells[2, 1].Value := 'East';
    Sheet.Cells[2, 2].Value := 1184350;
    Sheet.Cells[3, 1].Value := 'Central';
    Sheet.Cells[3, 2].Value := 902210;
    Sheet.Cells[4, 1].Value := 'West';
    Sheet.Cells[4, 2].Value := 1010675;

    // Frame anchored to rows 6..22, columns 1..8
    Chart := Sheet.AddChart(xlsxChartColumn, 'Revenue by Region', 6, 1, 22, 8);
    Chart.AddSeries('Revenue', 'Sales!$A$2:$A$4', 'Sales!$B$2:$B$4');
    Chart.ValueAxisTitle := 'USD';

    Sheet.AddImageFromFile(1, 5, 'logo.png');
    Book.SaveAs('dashboard.xlsx');
  finally
    Book.Free;
  end;
end;

Argumentas, kuris gali sukelti problemų, yra sekos eilutė, perduodama funkcijai AddSeries. Tai yra tiesioginis tekstas, užfiksuojamas iškvietimo metu, ir jis nežino, kad vėliau galite pridėti dar dvidešimt duomenų eilučių. Sukurkite šį rėžį pagal eilučių skaičių, kurį apskaičiavote jau įrašę duomenis, o ne prieš tai. Sklaidos (angl. scatter) ir burbulinėse (angl. bubble) diagramose šie du argumentai naudojami kitaip: kategorijų rėžis dabar nurodo X reikšmes, o reikšmių rėžis nurodo Y. Burbulo spindulys gaunamas iš trečiojo nuorodų rinkinio per BubbleSizeRange savybę grąžintame TXLSXChartSeries objekte. Naudodami šias diagramas galvokite apie argumentus kaip „X, Y, dydis“, o ne „kategorijos, reikšmės“.

TXLSXChartType apima stulpelines, juostines, linijines, skritulines, plokštumines, žiedines, sklaidos, burbulines ir radarines diagramas, kas visiškai padengia kasdienius ataskaitų poreikius. Norint gauti pilno puslapio diagramą be tinklelio, Book.AddChartSheet grąžina lapą, kurio savybė IsChartSheet yra True. Tai XLSX atitikmuo senajam diagramos lapui ir jam galioja ta pati taisyklė: nerašykite į jį langelių turinio.

Vaizdai įterpiami kaip baitai, o jų dydis nurodomas EMU vienetais

Paveikslėlio įterpimui yra skirtos dvi funkcijos, ir jų supainiojimas yra dažna problema kodo peržiūrose. Funkcija AddImage(ARow, ACol, AData, AFormat) reikalauja jau užkoduotų paveikslėlio baitų AData parametre (gryno PNG, JPEG, GIF ar BMP turinio). Perdavus jai failo kelią, išsaugosite tik keturiasdešimties simbolių tekstą, kurio jokia peržiūros programa negalės iškoduoti. Jei šaltinis yra failas diske, naudokite AddImageFromFile ir leiskite bibliotekai pačiai perskaityti baitus bei nustatyti formatą.

Toliau seka matmenų nustatymas. „DrawingML“ sistema nematuoja vaizdų pikseliais; ji naudoja „English Metric Units“ (EMU), kur 914 400 EMU sudaro colį, o esant 96 DPI skiriamajai gebai, 9 525 EMU atitinka vieną pikselį. Objektas TXLSXImage turi savybes WidthEMU ir HeightEMU. Todėl logotipui, kuris turėtų būti 180x60 pikselių dydžio, reikia nustatyti 1 714 500 x 571 500 EMU. Įrašykite šį perskaičiavimą į pavadintą konstantą. Tokie skaičiai kaip 1 714 500, išmėtyti kode, tampa neperskaitomi ir bus klaidingi vos tik kas nors pakeis tikslinį DPI. Beje, inkaravimo eilutė ir stulpelis prasideda nuo 1, kas atitinka kitus langelių API nustatymus, o ne nuo 0 prasidedančią EMU matematiką.

Diagramų lapai ir figūros senajame XLS formate

BIFF8 formatui skirta funkcija AddChartSheet priima diagramos tipą, ašių pavadinimus ir TXLSChartSeriesInfo įrašų masyvą, kur kiekvienas įrašas saugo pavadinimą bei kategorijų ir reikšmių rėžius kaip tekstą. Plaukiojančios figūros yra atskiras dalykas: jos pridedamos prie pačių duomenų darbalapio per Shapes kolekciją, o ne diagramos lape.

var
  Book: IXLSWorkbook;
  Data, Trend: IXLSWorksheet;
  Series: array[0..0] of TXLSChartSeriesInfo;
begin
  Book := TXLSWorkbook.Create;   // interface-counted: do not Free
  Data := Book.Sheets.Add;
  Data.Name := 'Data';
  Data.Cells.Item[1, 1].Value := 'Month';
  Data.Cells.Item[1, 2].Value := 'Units';
  Data.Cells.Item[2, 1].Value := 'Apr';
  Data.Cells.Item[2, 2].Value := 1530;
  Data.Cells.Item[3, 1].Value := 'May';
  Data.Cells.Item[3, 2].Value := 1721;

  Series[0].Name := 'Units';
  Series[0].Categories := 'Data!$A$2:$A$3';
  Series[0].Values := 'Data!$B$2:$B$3';
  Trend := Book.Sheets.AddChartSheet('Trend', xlsChartTypeLine,
    'Units sold', 'Month', 'Units', Series);
  // Trend is a chart substream: never call cell methods on it

  Data.Shapes.AddTextBox('Source: ERP nightly export', 6, 1, 8, 4);
  Data.Shapes.AddPicture('approved-stamp.bmp');
  Book.SaveAs('trend.xls');
end;

Čia svarbios dvi objektų gyvavimo trukmės detalės, kurios veikia priešingai. TXLSWorkbook valdomas per IXLSWorkbook sąsają ir yra skaičiuojamas nuorodomis (angl. reference-counted), todėl iškvietus Free rankiniu būdu įvyks dvigubas atlaisvinimas. Tuo tarpu TXLSXWorkbook iš ankstesnių skyrių yra paprastas objektas ir turi būti atlaisvinamas per try..finally bloką. Tas pats programuotojas, kuris pastebi trūkstamą Free XLSX pusėje, turi pastebėti ir perteklinį jo iškvietimą XLS pusėje. Pačios figūrų funkcijos yra vienodos: AddRectangle, AddOval ir AddLine su DeleteInRange leidžia išvalyti grafikos sritį. Visi jie inkaruojami eilutės ir stulpelio poromis, todėl įterpus eilutes virš jų, figūros pasislinks kartu su tinkleliu.

Dar vaizdų atvaizdavimui naudinga savybė seniesiems failams. TXLSPicture.TransparentColor paslepia pasirinktą bitų žemėlapio fono spalvą. Tai leidžia uždėti nestandartinį antspaudą (pavyzdžiui, „Patvirtinta“ antspaudą ar vandens ženklą) ant tinklelio formate, kurio BIFF atvaizdavimas nepalaiko PNG alfa kanalo. Nustatykite spalvą, kurios fone buvo sukurtas antspaudas, ir jį supantis stačiakampis išnyks.

Temų spalvos neišlieka BIFF8 formatu

OOXML grafikos užpildai gali rodyti į temos spalvos elementą, todėl pakeisti viso XLSX failo spalvų gamą pakeičiant temą yra labai paprasta. BIFF8 grafiniai įrašai neturi tokios galimybės. Kai „HotXLS“ taiko temos spalvą XLS grafikai, ji paverčiama į konkrečią RGB reikšmę ir išsaugoma. Temos indeksas prarandamas iškart įrašius failą, ir jį atidarius iš naujo jo atkurti nepavyks. Tai ypač aktualu ataskaitų generavimo įrankiams, kurie pritaiko tą patį dokumentą skirtingiems klientams. Laikykite temų ir RGB spalvų atitikmenis savo konfigūracijoje ir taikykite juos generavimo metu, o ne tikėkitės nuskaityti iš išsaugoto XLS failo.

Kitas svarbus sprendimas susijęs su našumu. XLS sąsajai galima nurodyti visiškai praleisti grafikos sluoksnio apdorojimą, kai iš didelio failo reikia tik langelių duomenų. Tai atliekama nustatant _DisableGraphics reikšmę į True ir sutaupo daug laiko skaitant didelius kiekius. Tačiau atminkite: tokiu būdu atidaryta knyga neturės „OfficeArt“ srauto atmintyje, todėl ją išsaugojus visi brėžiniai bus ištrinti. Naudokite šią vėliavėlę tik skaitymo užduotims. Daugiau apie našumą skaitykite straipsnyje pastabos apie didelių darbalapių našumą su HotXLS.

Inkarų stabilumo išsaugojimas keičiantis tinkleliui

Ataskaitos retai lieka to paties dydžio, kokio buvo sugeneruotos, ir čia inkarų modelis parodo savo privalumus. XLSX sąsajos struktūrinės operacijos (InsertRows, DeleteRows ir atitinkami stulpelių metodai) perkelia priklausomus sluoksnius kartu su langeliais. Sujungtos sritys, nuorodos, komentarai, užfiksuotos sritys, filtrų rėžiai, sąlyginis formatavimas, tikrinimai, lentelės, pavadinimai ir paveikslėlių bei diagramų inkarai juda kartu. Logotipas, inkaruotas pirmoje eilutėje, lieka viršuje, kai po juo įterpiama dešimt eilučių. Diagramas rėmelis po duomenų bloku pasislenka žemyn, kai duomenų blokas auga. Vienintelis dalykas, kuris nėra atnaujinamas, yra sekos rėžio eilutės, kurias užfiksavote kaip tekstą prieš įterpimą. Todėl saugus duomenų pildymo kelias yra toks: pirmiausia įrašykite ir transformuokite duomenis, o tik pačioje pabaigoje kurkite diagramas ir dėkite vaizdus, naudodami rėžius, gautus jau po visų eilučių įterpimo.

Du papildomi įrankiai užbaigia šį rinkinį. TXLSTextBox.SetArea XLS pusėje leidžia perstatyti esamą teksto laukelį ar figūrą į naują langelių stačiakampį, kas yra patogiau nei jo trynimas ir kūrimas iš naujo pasislinkus porštei. O AddPicture funkcija priima tiesioginį TBitmap objektą su pasirinktine skaidrumo vėliavėle, todėl viskas, ką galite nupiešti per VCL kodą (indikatorius, mini grafikai ar nestandartinės diagramos), gali būti patalpinta tiesiai į lapą nesaugant laikinų failų diske.

Diagramos ir vaizdai beveik visada yra paskutinis ataskaitos sluoksnis, todėl teisingas paruošimas lemia, ar jie bus įterpti sėkmingai. Duomenų pildymas aprašytas straipsnyje ataskaitų generavimas pagal šablonus, o tinklelio stabilumo palaikymas po inkarais aprašytas straipsnyje sujungti langeliai ir šablonų išdėstymo valdymas. Pilną klasių ir metodų dokumentaciją rasite „HotXLS Component“ produkto puslapyje HotXLS Component.