Τεχνικό Άρθρο

Excel Sparklines στο Delphi: Το HotXLS και η επέκταση x14

Το HotXLS δημιουργεί Excel Sparklines — τα μικροσκοπικά γραφήματα που σχεδιάζει το Excel μέσα σε ένα μόνο κελί — από Delphi και C++Builder με μία κλήση: η μέθοδος TXLSXWorksheet.AddSparklineGroup δημιουργεί μια ομάδα sparkline πάνω από ένα εύρος δεδομένων, και η αποθήκευση του βιβλίου εργασίας την εγγράφει ως μπλοκ επέκτασης x14 μέσα στο extLst του φύλλου εργασίας, το οποίο το Excel 2010 και μεταγενέστερες εκδόσεις αποδίδουν, ενώ οι παλαιότεροι αναγνώστες το παρακάμπτουν χωρίς παράπονα. Χωρίς αντικείμενα γραφημάτων, χωρίς μέρη σχεδίασης, χωρίς άγκυρες

Το σενάριο που κάνει τα sparklines να αξίζουν τον κόπο είναι πάντα το ίδιο. Ένα βιβλίο εργασίας πίνακα ελέγχου (dashboard) περιέχει μερικές εκατοντάδες γραμμές, μία ανά προϊόν ή περιοχή ή λογαριασμό, με δώδεκα μηνιαία νούμερα η καθεμία, και κάποιος θέλει ένα γλύφο τάσης στο τέλος κάθε γραμμής. Αν το μοντελοποιήσετε αυτό με πραγματικά γραφήματα, δημιουργείτε εκατοντάδες μέρη γραφημάτων DrawingML, το καθένα με το δικό του μέρος XML, σχέση και γεωμετρία αγκύρωσης, για να μην πούμε τίποτα για το τι κάνει αυτό στο μέγεθος του αρχείου και στο χρόνο ανοίγματος. Μια ομάδα sparkline είναι ένα μπλοκ XML που λέει "σχεδίασε μια μικροσκοπική γραμμή από το B2:M2 στο N2, και από το B3:M3 στο N3", επαναλαμβανόμενη οικονομικά για όσες γραμμές έχετε

Γιατί τα sparklines είναι μια επέκταση x14 αντί για γράφημα;

Τα sparklines εμφανίστηκαν με το Excel 2010, τρία χρόνια μετά το πάγωμα του βασικού σχήματος ECMA-376, οπότε δεν θα μπορούσαν να ζήσουν μέσα στην κατάλληλη σήμανση του φύλλου εργασίας χωρίς να χαλάσουν κάθε υπάρχοντα αναγνώστη. Η απάντηση της Microsoft είναι ο μηχανισμός λίστας επεκτάσεων: ένα φύλλο εργασίας μπορεί να τελειώνει με ένα στοιχείο <extLst> που περιέχει μπλοκ <ext>, καθένα επισημασμένο με ένα URI που προσδιορίζει τη δυνατότητα ([MS-XLSX] §2.3.6). Τα sparklines χρησιμοποιούν το URI {05C60535-1F16-4fd2-B633-F4F36F0B64E0} και το namespace x14. Ένας αναγνώστης που αναγνωρίζει το GUID αναλύει το μπλοκ· ένας αναγνώστης που δεν το αναγνωρίζει απαιτείται να το παρακάμψει. Αυτή είναι όλη η ιστορία της συμβατότητας προς τα εμπρός, και γι' αυτό το Excel 2007 ανοίγει καθαρά ένα βιβλίο εργασίας με sparklines — απλά δείχνει τους αριθμούς προέλευσης χωρίς τους γλύφους

Μια λεπτομέρεια του μηχανισμού αξίζει να επισημανθεί επειδή έρχεται σε αντίθεση με μια κοινή υπόθεση: δεν απαιτείται δήλωση mc:Ignorable στη ρίζα του φύλλου εργασίας. Η πύλη GUID ext είναι επαρκής από μόνη της, και ο συγγραφέας του ίδιου του Excel δηλώνει το namespace x14 τοπικά στο στοιχείο <ext> αντί να αγγίξει τη ρίζα. Το HotXLS ακολουθεί το ίδιο μοτίβο, το οποίο διατηρεί το υπόλοιπο XML του φύλλου εργασίας ανέπαφες. Αυτή είναι επίσης η δομική διαφορά από τα πραγματικά γραφήματα: ένα γράφημα είναι ένα ξεχωριστό μέρος DrawingML με τη δική του ροή απόδοσης, που καλύπτεται στο συνοδευτικό άρθρο για γραφήματα, εικόνες και σχέδια, ενώ ένα sparkline είναι μεταδεδομένα φύλλου εργασίας που σχεδιάζει το ίδιο το πλέγμα του υπολογιστικού φύλλου

Πώς προσθέτετε sparklines σε ένα αρχείο XLSX προγραμματιστικά;

Η μέθοδος TXLSXWorksheet.AddSparklineGroup(AType, ASourceRange, ALocation) είναι η διαδρομή μίας κλήσης: δημιουργεί μια ομάδα του δεδομένου τύπου, την αρχικοποιεί με το προεπιλεγμένο χρώμα σειράς του Excel και προσθέτει το πρώτο sparkline που αντιστοιχίζει ένα εύρος προέλευσης με προσδιορισμένο φύλλο σε ένα κελί υποδοχής. Το επιστρεφόμενο TXLSXSparklineGroup δέχεται στη συνέχεια περισσότερα sparklines μέσω της μεθόδου του Add, ένα ζεύγος F/Sqref ανά γραμμή, που μοιράζονται όλα τον τύπο και τη μορφοποίηση της ομάδας. Για την περίπτωση του πίνακα ελέγχου (dashboard) αυτό είναι ακριβώς το σχήμα που θέλετε: μία ομάδα, ένα σύνολο οπτικών επιλογών, εκατοντάδες μέλη

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  Grp: TXLSXSparklineGroup;
  Row: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Book.Open('dashboard.xlsx');
    Sheet := Book.Sheets[0];

    // One group: every product row shares type and formatting
    Grp := Sheet.AddSparklineGroup(xlsxSparklineLine,
      'Sheet1!B2:M2', 'N2');
    for Row := 3 to 201 do
      Grp.Add(Format('Sheet1!B%d:M%d', [Row, Row]),
        Format('N%d', [Row]));

    Book.SaveAs('dashboard-trends.xlsx');
  finally
    Book.Free;
  end;
end;

Το TXLSXSparklineType καλύπτει τα τρία είδη που προσφέρει το Excel: xlsxSparklineLine για γραμμές τάσης, xlsxSparklineColumn για μικροσκοπικά γραφήματα ράβδων, και xlsxSparklineStacked για λωρίδες win/loss όπου κάθε τιμή αποδίδεται ως μπλοκ προς τα πάνω ή προς τα κάτω. Κάθε μέλος sparkline αποτελείται από δύο μόνο συμβολοσειρές — F, το εύρος προέλευσης με προσδιορισμένο φύλλο, και Sqref, το κελί στο οποίο σχεδιάζεται ο γλύφος — επομένως δεν υπάρχει τίποτα άλλο να ρυθμιστεί ανά γραμμή. Οι ομάδες μπορούν επίσης να δημιουργηθούν εξ ολοκλήρου με το χέρι μέσω της συλλογής Worksheet.SparklineGroups όταν θέλετε να ξεκινήσετε από μια κενή ομάδα αντί για την αρχικοποιημένη προεπιλογή

Δείκτες, υποδοχές χρωμάτων και κλιμάκωση αξόνων στο πληκτρολογημένο μοντέλο

Grp := Sheet.AddSparklineGroup(xlsxSparklineStacked,
  'Sheet1!B2:M2', 'N2');
Grp.NegativePoints := True;          // losing months stand out
Grp.ColorNegative := $FFD00000;
Grp.HighPoint := True;
Grp.ColorHigh := $FF00B050;
Grp.MinAxisType := xlsxSparklineAxisGroup;   // one scale for all rows
Grp.MaxAxisType := xlsxSparklineAxisGroup;
Grp.DisplayEmptyCellsAs := xlsxSparklineEmptyGap;

Οι ιδιότητες του άξονα αξίζουν μια στιγμή σκέψης πριν αποδεχτείτε τις προεπιλογές. Τα MinAxisType και MaxAxisType παίρνουν το καθένα μεμονωμένη, ομαδική ή προσαρμοσμένη κλιμάκωση: η μεμονωμένη τεντώνει κάθε sparkline στο δικό του ελάχιστο και μέγιστο, γεγονός που κάνει μια γραμμή που ταλαντεύεται μεταξύ 99 και 101 να φαίνεται τόσο δραματική όσο μια γραμμή που τριπλασιάζεται. Η ομαδική κλιμάκωση τοποθετεί κάθε μέλος σε μια κοινή κατακόρυφη κλίμακα, έτσι ώστε το ύψος του γλύφου να σημαίνει την ίδια ποσότητα σε κάθε γραμμή — συνήθως αυτό που θέλει ένας συγκριτικός πίνακας ελέγχου. Το DisplayEmptyCellsAs αποφασίζει τι κάνει ένα κενό κελί προέλευσης σε μια γραμμή: αφήνει κενό, μετράει ως μηδέν ή γεφυρώνει την τρύπα συνδέοντας τους γείτονές του

Τι γράφεται στο extLst του φύλλου εργασίας κατά την αποθήκευση

Κατά την αποθήκευση, το HotXLS σειριοποιεί κάθε ομάδα ως στοιχείο x14:sparklineGroup που φέρει τα χαρακτηριστικά τύπου και εναλλαγής, τις υποδοχές χρωμάτων ως θυγατρικά στοιχεία, και μια λίστα x14:sparklines της οποίας τα μέλη συνδυάζουν έναν τύπο εύρους xm:f με ένα κελί υποδοχής xm:sqref. Ολόκληρη η δομή βρίσκεται μέσα στο <extLst><ext uri="{05C60535-1F16-4fd2-B633-F4F36F0B64E0}"> στο τέλος του μέρους του φύλλου εργασίας, με το namespace x14 να δηλώνεται ακριβώς πάνω στο μπλοκ, ακριβώς όπως το γράφει το Excel. Ένα φύλλο εργασίας χωρίς ομάδες sparkline δεν εκπέμπει καθόλου extLst, οπότε τα αρχεία που δεν χρησιμοποίησαν ποτέ τη δυνατότητα βγαίνουν byte προς byte πανομοιότυπα με αυτά που παρήγαγαν οι προηγούμενες εκδόσεις του HotXLS — η ίδια πειθαρχία συντηρητικού συγγραφέα που περιγράφεται στο άρθρο για την υποδομή lossless round-trip

Μια λεπτομέρεια των προδιαγραφών έχει σημασία εάν συγκρίνετε ποτέ το αποτέλεσμα με αυτό του Excel ή ελέγχετε το XML με το χέρι: η προεπιλογή του σχήματος για το lineWeight είναι 0,75 σημεία, όχι το 1,25 που αναφέρουν ορισμένες δευτερεύουσες πηγές — το [MS-XLSX] ορίζει το CT_SparklineGroup@lineWeight με προεπιλογή 0,75, ταιριάζοντας με το πάχος που εφαρμόζει πραγματικά το UI του Excel. Το HotXLS σέβεται την προεπιλογή με παράλειψη: ορίστε το LineWeight σε οτιδήποτε άλλο εκτός από 0,75 και το χαρακτηριστικό γράφεται, αφήστε το στο 0,75 και δεν γράφεται. Αν κάνετε λάθος σε αυτό προς οποιαδήποτε κατεύθυνση, παράγονται αρχεία που αποδίδονται ελαφρώς βαρύτερα ή ελαφρύτερα από το ίδιο βιβλίο εργασίας που αποθηκεύτηκε από το Excel

Κύκλοι round-trip, αντίγραφα φύλλων και επεκτάσεις που το HotXLS δεν μοντελοποιεί

Το άνοιγμα είναι η κατοπτρική εικόνα της αποθήκευσης. Όταν το HotXLS ανοίγει ένα βιβλίο εργασίας που φέρει sparklines — είτε το έγραψε το HotXLS είτε το Excel — ο αναλυτής (parser) πύλες στο sparkline ext GUID και ανακατασκευάζει κάθε ομάδα, μέλος, χρώμα και επιλογή άξονα στο πληκτρολογημένο μοντέλο, οπότε ένας κύκλος άνοιγμα→επεξεργασία→αποθήκευση τα διατηρεί πλήρως. Η αντιγραφή ενός φύλλου εργασίας σε ένα άλλο βιβλίο εργασίας κλωνοποιεί τις ομάδες sparkline μαζί με το υπόλοιπο φύλλο, γεγονός που καθιστά τη δημιουργία αναφορών βάσει προτύπων απλή: κρατήστε ένα μορφοποιημένο φύλλο προτύπου, αντιγράψτε το ανά αναφορά και επανακαθορίστε τα εύρη

Το extLst μπορεί επίσης περιέχει μπλοκ επεκτάσεων για τα οποία το HotXLS δεν έχει πληκτρολογημένο μοντέλο — το Excel γράφει μπλοκ x14 για δυνατότητες όπως slicers και εκτεταμένη μορφοποίηση υπό όρους στην ίδια λίστα. Από την έκδοση v2.131.0 αυτά τα ξένα μπλοκ διατηρούνται κατά λέξη σε έναν κύκλο round-trip αντί να απορρίπτονται: ο αναλυτής τα συλλαμβάνει, τα εκθέτει μόνο για ανάγνωση μέσω του Worksheet.RawWorksheetExts, και τα γράφει πίσω κατά την αποθήκευση, και ταξιδεύουν με ένα φύλλο όταν αυτό αντιγράφεται σε άλλο βιβλίο εργασίας. Το διατηρημένο XML ανακατασκευάζεται από συμβάντα αναλυτή, επομένως είναι σημασιολογικά ισοδύναμο με το πρωτότυπο και όχι πανομοιότυπο σε byte, αλλά το Excel το δέχεται και οι δυνατότητες επιβίνουν. Η πρακτική συνέπεια είναι αυτή που έχει σημασία στην παραγωγή: το άνοιγμα του βιβλίου εργασίας ενός πελάτη και η εκ νέου αποθήκευσή του δεν αφαιρεί πλέον επεκτάσεις που δεν ζητήσατε από το HotXLS να κατανοήσει

Η ειλικρινής οριακή γραμμή: τα sparklines αποδίδονται στο Excel 2010 και μεταγενέστερα, και σε οποιονδήποτε άλλο αναγνώστη που εφαρμόζει την επέκταση x14· το Excel 2007 δείχνει τα δεδομένα χωρίς τους γλύφους, η οποία είναι η υποβάθμιση που σχεδιάστηκε να έχει η μορφή. Οι τρεις τύποι sparkline, οι οκτώ υποδοχές χρωμάτων, οι εναλλαγές σημείων και οι τρεις λειτουργίες αξόνων καλύπτουν ολόκληρη την επιφάνεια επιλογών που προσφέρει ο ίδιος ο διάλογος του Excel. Η υποστήριξη Sparkline, μαζί με την υπόλοιπη μηχανή XLSX που εμφανίζεται εδώ, παρέχεται στο HotXLS Delphi Excel Component για Delphi και C++Builder