בחלק הקודם, יצרתי את הסביבה הגרפית ליצירת הגרף, בחלק הזה אתמקד בכל מה שקשור להכנה ליצירת הגרף עצמו, ובחלק הבא (שהוא האחרון), אתמקד בגרף עצמו וביצוא שלו לתמונה.
כתיבת קוד
החלטתי קודם להתעסק קצת ב"קוסמטיקה" שהיא קצת חשובה. הקוסמטיקה היא הוספה והסרה של שורות, וכן התגובתיות של הרכיבים. אבל הם חשובים מאוד כי רק באמצעותם ניתן בעצם ליצר את הגרף עצמו, וכן לאפשר או לא לאפשר פעולות למשתמש.
הלכתי קודם לכפתור הנקרא btnAddItem , לחצתי עליו פעמים, והוא פתח בעורך הטקסט מתודת Callback בשם btnAddItemClick. ניתן לראות אותה גם בObject Inspector במידה ונלך לטאב הנקרא Events. זהו אירוע ברירת המחדל של כפתור, ולכן הוא זה שבעצם הוגדר.
הקוד עבור הcallback מאוד פשוט. אנחנו הולכים להוסיף לו 2 שורות. הראשונה אומרת להוסיף עוד שורה, השניה הולכת לשים מספור מתאים לתא, ובכך נקבל מספורים כדוגמת "1, 2, 3" וכו' … פר שורה:
procedure TForm1.btnAddItemClick(Sender: TObject);
begin
sgGraphDetails.RowCount := sgGraphDetails.RowCount + 1;
sgGraphDetails.Cells[0,sgGraphDetails.RowCount -1] := IntToStr(sgGraphDetails.RowCount -1);
end;
השלב הבא יהיה להסיר שורה על ידי הוספת Callback עבור btnRemoveLastItem:
procedure TForm1.btnRemoveLastItemClick(Sender: TObject);
begin
if sgGraphDetails.RowCount > 1 then
sgGraphDetails.RowCount := sgGraphDetails.RowCount - 1;
end;
אנחנו גם בודקים, אם אנחנו לא חורגים מהגבולות שלנו, כאשר כאן מדובר בשאלה האם יש לנו יותר משורה בודדה, ורק אם כן, נבטל שורה אחת.
עכשיו נלך לעשות "טריק" פשוט. הפעלת וביטול פעולות של מחיקה ורינדור. אם אין מידע, או שורות, אני לא רוצה לעשות סתם פעולות, אשר לפחות ברינדור, הן לוקחות כוח מיחשוב שיכול להיות גדול, ובהסרת שורות, במידה ואין אפשרות, סתם תהיה "פרצה הקוראת לגנב".
לשם כך, הלכתי לטאב הEvents בחרתי ברכיב של sgGraphDetails, ובאירוע OnColRowDeleted. לחצתי עליו פעמיים, וכתבתי את הקוד הבא:
procedure TForm1.sgGraphDetailsColRowDeleted(Sender: TObject; IsColumn: Boolean;
sIndex, tIndex: Integer);
begin
btnRemoveLastItem.Enabled := sgGraphDetails.RowCount > 1;
btnApply.Enabled := sgGraphDetails.RowCount > 1;
end;
אני מבצע בדיקה לוגית (במקום שאלת if), האם יש יותר משורה אחת בודדה, ואם כן, רק אז לאפשר את הכפתורים.
אבל זה לא מספיק ! הרי מה קורה כאשר אני מוסיף שורה ?
אז חזרתי לטאב הEvents של sgGraphDetails, ובאירוע OnColRowInserted בחרתי מהרשימה את sgGraphDetailsColRowDeleted, ולמעשה יש לי שני Callbacks אשר מצביעים על אותו הקוד, רק מתרחשים בזמנים שונים.
השלב הבא הוא לחשוב מה קורה כאשר אנחנו בעצם רק יוצרים את החלון. הרי הכפתורים בברירת המחדל מאופשרים, אבל לא באמת ניתן למחוק או לרנדר משהו.
אז הלכתי לאירוע של הטופס עצמו, ויצרתי את הקוד הבא, ועל הדרך גם הוספתי עוד כותרת אחת רק בקוד:
procedure TForm1.FormCreate(Sender: TObject);
begin
sgGraphDetails.Cells[0,0] := '#';
btnRemoveLastItem.Enabled := sgGraphDetails.RowCount > 1;
btnApply.Enabled := sgGraphDetails.RowCount > 1;
end;
עד כאן הקוד מאוד פשוט נכון ?
זוכרים שזרקתי על החלון, רכיב דיאלוג של צבעים ?
הגיע הזמן להשתמש בו, ולקשר אותו בעצם לטבלה ששמתי.
אז הלכתי לאירוע של OnButtonClick ברכיב sgGraphDetails ולחצתי עליו פעמיים.
שם בעצם אני בודק קודם כל מה העמודה שלחצו בה, ורק אם זו העמודה הנכונה, אני מציג את הדיאלוג.
לאחר הצגת הדיאלוג, במידה והמשתמש מאפשר לי (כלומר לא לחץ על cancel), אני מוסיף את הצבע הנבחר.
התאוריה נחמדה, והיא נראת ככה בצורה מעשית:
procedure TForm1.sgGraphDetailsButtonClick(Sender: TObject; aCol, aRow: Integer);
begin
if (aRow >= 1) and (aCol = 3) then
begin
if ColorDialog.Execute then
begin
sgGraphDetails.Cells[aCol, aRow] := ColorToString(ColorDialog.Color);
end;
end;
end;
אם חושבים על זה לעומק, הבעיה החמורה ביותר שיש היא האפשרות שהאחוזים לא יהיו מספר עשרוני.
אש צריך להגיד בקוד שבמידה ומזינים ערך לא חוקי, פשוט לא לקבל את זה בעת ההזנה (ולא אחרי).
לשם כך החלטתי לממש את זה דווקא באירוע בשם OnSetEditText. הסיבה לכך פשוטה. אני רוצה שבזמן הקביעה מה הוא העריכה, אני אחליט אם זה תקין או לא:
procedure TForm1.sgGraphDetailsSetEditText(Sender: TObject; ACol, ARow: Integer;
const Value: string);
var num : double;
begin
if (ACol = 2) and (Value '') then
begin
if not TryStrToFloat(Value, num) then // do we have a real floating point number ?
begin
sgGraphDetails.Cells[ACol, ARow] := '';
end;
end;
end;
כאן אני מנסה להמיר בזמן העריכה את הערך שהתקבל. במידה והוא מתאים למספר עשרוני, אני לא נוגע בתוכן, אבל במידה והוא לא, אז אני הופך את התא לריק.
אם נרצה ליצור תוכנה מלאה, ולא רק הדגמה, אבדוק כאן כי העמודה של הצבעים גם מכילה טקסט תקין עבור צבע. אך במקרה הזה, החלטתי שלא להוסיף לכך קוד.
בחלק הבא (כאמור), אציג כיצד להגיע תאכלס ליצירת הגרף.